Merge branch 'master' into development

pull/2048/head
xxDUxx 2022-07-15 20:23:02 +02:00 committed by GitHub
commit eb2565d763
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
289 changed files with 11453 additions and 790 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]
@ -6,29 +6,25 @@ jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
steps:
- name: Checkout repository and submodules
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
submodules: recursive
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
- name: Use Node.js 16.x
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: install testing dependencies
run: npm i
- name: test all apps and widgets
run: npm run test
- name: install typescript dependencies
node-version: 16.x
- name: Install testing dependencies
run: npm ci
- name: Test all apps and widgets
run: npm test
- name: Install typescript dependencies
working-directory: ./typescript
run: npm ci
- name: build types
- name: Build types
working-directory: ./typescript
run: npm run build:types
- name: build all TS apps and widgets
- name: Build all TS apps and widgets
working-directory: ./typescript
run: npm run build
run: npm run build

1
.gitignore vendored
View File

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

View File

@ -1,7 +1,7 @@
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 **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"]={
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
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

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
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
draw:draw // called to draw the widget
};

View File

@ -3,3 +3,5 @@
0.03: Do not alarm while charging
0.04: Obey system quiet mode
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.07: Fix bug on the cutting edge firmware

View File

@ -11,4 +11,5 @@ Different settings can be personalized:
- Dismiss delay: Delay added before the next alert if the alert is dismissed. From 5 to 60 min
- Pause delay: Same as Dismiss delay but longer (usefull for meetings and such). From 30 to 240 min
- Min steps: Minimal amount of steps to count as an activity
- Temp Threshold: Temperature threshold to determine if the watch is worn

View File

@ -1,42 +1,46 @@
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();
(function () {
// load variable before defining functions cause it can trigger a ReferenceError
const activityreminder = require("activityreminder");
const storage = require("Storage");
const activityreminder_settings = activityreminder.loadSettings();
let activityreminder_data = activityreminder.loadData();
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:
if (!(storage.readJSON('setting.json', 1) || {}).quiet) {
Bangle.buzz(400);
}
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();
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
run();
})();

View File

@ -1,45 +1,70 @@
function run() {
if (isNotWorn()) return;
let now = new Date();
let h = now.getHours();
let health = Bangle.getHealthStatus("day");
if (h >= activityreminder_settings.startHour && h < activityreminder_settings.endHour) {
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 10 secs after the stepsDate + minSteps
cancel all other timers of this app
*/
}
if(activityreminder.mustAlert(activityreminder_data, activityreminder_settings)){
load('activityreminder.app.js');
}
}
}
function isNotWorn() {
// todo in a futur release check temperature and mouvement in a futur release
return Bangle.isCharging();
}
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;
(function () {
// load variable before defining functions cause it can trigger a ReferenceError
const activityreminder = require("activityreminder");
const activityreminder_settings = activityreminder.loadSettings();
let activityreminder_data = activityreminder.loadData();
if (activityreminder_data.firstLoad) {
activityreminder_data.firstLoad = false;
activityreminder.saveData(activityreminder_data);
}
setInterval(run, 60000);
/* todo in a futur release
increase setInterval time to something that is still sensible (5 mins ?)
add settimer to trigger like 10 secs after the stepsDate + minSteps
cancel all other timers of this app
*/
}
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 () {
return Object.assign({
enabled: true,
@ -8,21 +6,22 @@ exports.loadSettings = function () {
maxInnactivityMin: 30,
dismissDelayMin: 15,
pauseDelayMin: 120,
minSteps: 50
}, storage.readJSON("activityreminder.s.json", true) || {});
minSteps: 50,
tempThreshold: 27
}, require("Storage").readJSON("activityreminder.s.json", true) || {});
};
exports.writeSettings = function (settings) {
storage.writeJSON("activityreminder.s.json", settings);
require("Storage").writeJSON("activityreminder.s.json", settings);
};
exports.saveData = function (data) {
storage.writeJSON("activityreminder.data.json", data);
require("Storage").writeJSON("activityreminder.data.json", data);
};
exports.loadData = function () {
let health = Bangle.getHealthStatus("day");
const data = Object.assign({
let data = Object.assign({
firstLoad: true,
stepsDate: new Date(),
stepsOnDate: health.steps,
@ -30,7 +29,7 @@ exports.loadData = function () {
dismissDate: new Date(1970),
pauseDate: new Date(1970),
},
storage.readJSON("activityreminder.data.json") || {});
require("Storage").readJSON("activityreminder.data.json") || {});
if(typeof(data.stepsDate) == "string")
data.stepsDate = new Date(data.stepsDate);

View File

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

View File

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

@ -29,3 +29,5 @@
0.27: New UI!
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.30: Fix "Enable All"
0.31: Add seconds to timers

View File

@ -86,7 +86,8 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex) {
const menu = {
"": { "title": isNew ? /*LANG*/"New Alarm" : /*LANG*/"Edit Alarm" },
"< Back": () => {
saveAlarm(alarm, alarmIndex, time);
prepareAlarmForSave(alarm, alarmIndex, time);
saveAndReload();
showMainMenu();
},
/*LANG*/"Hour": {
@ -144,7 +145,7 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex) {
E.showMenu(menu);
}
function saveAlarm(alarm, alarmIndex, time) {
function prepareAlarmForSave(alarm, alarmIndex, time) {
alarm.t = require("time_utils").encodeTime(time);
alarm.last = alarm.t < require("time_utils").getCurrentTimeMillis() ? new Date().getDate() : 0;
@ -153,8 +154,6 @@ function saveAlarm(alarm, alarmIndex, time) {
} else {
alarms[alarmIndex] = alarm;
}
saveAndReload();
}
function saveAndReload() {
@ -251,7 +250,8 @@ function showEditTimerMenu(selectedTimer, timerIndex) {
const menu = {
"": { "title": isNew ? /*LANG*/"New Timer" : /*LANG*/"Edit Timer" },
"< Back": () => {
saveTimer(timer, timerIndex, time);
prepareTimerForSave(timer, timerIndex, time);
saveAndReload();
showMainMenu();
},
/*LANG*/"Hours": {
@ -268,11 +268,20 @@ function showEditTimerMenu(selectedTimer, timerIndex) {
wrap: true,
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": {
value: timer.on,
onchange: v => timer.on = v
},
/*LANG*/"Vibrate": require("buzz_menu").pattern(timer.vibrate, v => timer.vibrate = v),
/*LANG*/"Cancel": () => showMainMenu()
};
if (!isNew) {
@ -293,7 +302,7 @@ function showEditTimerMenu(selectedTimer, timerIndex) {
E.showMenu(menu);
}
function saveTimer(timer, timerIndex, time) {
function prepareTimerForSave(timer, timerIndex, time) {
timer.timer = require("time_utils").encodeTime(time);
timer.t = require("time_utils").getCurrentTimeMillis() + timer.timer;
timer.last = 0;
@ -303,8 +312,6 @@ function saveTimer(timer, timerIndex, time) {
} else {
alarms[timerIndex] = timer;
}
saveAndReload();
}
function showAdvancedMenu() {
@ -327,7 +334,16 @@ function enableAll(on) {
} else {
E.showPrompt(/*LANG*/"Are you sure?", { title: on ? /*LANG*/"Enable All" : /*LANG*/"Disable All" }).then((confirm) => {
if (confirm) {
alarms.forEach(alarm => alarm.on = on);
alarms.forEach((alarm, i) => {
alarm.on = on;
if (on) {
if (alarm.timer) {
prepareTimerForSave(alarm, i, require("time_utils").decodeTime(alarm.timer))
} else {
prepareAlarmForSave(alarm, i, require("time_utils").decodeTime(alarm.t))
}
}
});
saveAndReload();
showMainMenu();
} else {

View File

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

View File

@ -90,6 +90,35 @@
sched.setAlarms(alarms);
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];
if (h) h(); else console.log("GB Unknown",event);

View File

@ -2,7 +2,7 @@
"id": "android",
"name": "Android Integration",
"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.",
"icon": "app.png",
"tags": "tool,system,messages,notifications,gadgetbridge",
@ -15,6 +15,6 @@
{"name":"android.img","url":"app-icon.js","evaluate":true},
{"name":"android.boot.js","url":"boot.js"}
],
"data": [{"name":"android.settings.json"}],
"data": [{"name":"android.settings.json"}, {"name":"android.calendar.json"}],
"sortorder": -8
}

View File

@ -9,3 +9,6 @@
0.09: Fix time/date disappearing after fullscreen notification
0.10: Use ClockFace library
0.11: Use ClockFace.is12Hour
0.12: Add settings to hide date,widgets
0.13: Add font setting
0.14: Use ClockFace_menu.addItems

View File

@ -4,3 +4,7 @@ A simple digital clock showing seconds as a horizontal bar.
| 24hr style | 12hr style |
| --- | --- |
| ![24-hour bar clock](screenshot.png) | ![12-hour bar clock with meridian](screenshot_pm.png) |
## Settings
* `Show date`: display date at the bottom of screen
* `Font`: choose between bitmap or vector fonts

View File

@ -51,24 +51,25 @@ function dateText(date) {
const ClockFace = require("ClockFace"),
clock = new ClockFace({
precision:1,
settingsFile:'barclock.settings.json',
init: function() {
const Layout = require("Layout");
this.layout = new Layout({
type: "v", c: [
{
type: "h", c: [
{id: "time", label: "88:88", type: "txt", font: "6x8:5", col:g.theme.fg, bgCol: g.theme.bg}, // size updated below
{id: "time", label: "88:88", type: "txt", font: "6x8:5", col:g.theme.fg, bgCol: g.theme.bg}, // updated below
{id: "ampm", label: " ", type: "txt", font: "6x8:2", col:g.theme.fg, bgCol: g.theme.bg},
],
},
{id: "bar", type: "custom", fraction: 0, fillx: 1, height: 6, col: g.theme.fg2, render: renderBar},
{height: 40},
{id: "date", type: "txt", font: "10%", valign: 1},
this.showDate ? {height: 40} : {},
this.showDate ? {id: "date", type: "txt", font: "10%", valign: 1} : {},
],
}, {lazy: true});
// adjustments based on screen size and whether we display am/pm
let thickness; // bar thickness, same as time font "pixel block" size
if (this.is12Hour) {
if (this.is12Hour && locale.hasMeridian) {
// Maximum font size = (<screen width> - <ampm: 2chars * (2*6)px>) / (5chars * 6px)
thickness = Math.floor((Bangle.appRect.w-24)/(5*6));
} else {
@ -76,13 +77,23 @@ const ClockFace = require("ClockFace"),
thickness = Math.floor(Bangle.appRect.w/(5*6));
}
this.layout.bar.height = thickness+1;
this.layout.time.font = "6x8:"+thickness;
if (this.font===1) { // vector
const B2 = process.env.HWVERSION>1;
if (this.is12Hour && locale.hasMeridian) {
this.layout.time.font = "Vector:"+(B2 ? 50 : 60);
this.layout.ampm.font = "Vector:"+(B2 ? 20 : 40);
} else {
this.layout.time.font = "Vector:"+(B2 ? 60 : 80);
}
} else {
this.layout.time.font = "6x8:"+thickness;
}
this.layout.update();
},
update: function(date, c) {
if (c.m) this.layout.time.label = timeText(date);
if (c.h) this.layout.ampm.label = ampmText(date);
if (c.d) this.layout.date.label = dateText(date);
if (c.d && this.showDate) this.layout.date.label = dateText(date);
const SECONDS_PER_MINUTE = 60;
if (c.s) this.layout.bar.fraction = date.getSeconds()/SECONDS_PER_MINUTE;
this.layout.render();

View File

@ -1,7 +1,7 @@
{
"id": "barclock",
"name": "Bar Clock",
"version": "0.11",
"version": "0.14",
"description": "A simple digital clock showing seconds as a bar",
"icon": "clock-bar.png",
"screenshots": [{"url":"screenshot.png"},{"url":"screenshot_pm.png"}],
@ -12,6 +12,10 @@
"allow_emulator": true,
"storage": [
{"name":"barclock.app.js","url":"clock-bar.js"},
{"name":"barclock.settings.js","url":"settings.js"},
{"name":"barclock.img","url":"clock-bar-icon.js","evaluate":true}
],
"data": [
{"name":"barclock.settings.json"}
]
}

26
apps/barclock/settings.js Normal file
View File

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

9
apps/barcode/ChangeLog Normal file
View File

@ -0,0 +1,9 @@
0.01: Please forgive me
0.02: Now tells time!
0.03: Interaction
0.04: Shows day of week
0.05: Shows day of month
0.06: Updates every 5 minutes when locked, or when unlock occurs. Also shows nr of steps.
0.07: Step count resets at midnight
0.08: Step count stored in memory to survive reloads. Now shows step count daily and since last reboot.
0.09: NOW it really should reset daily (instead of every other day...)

23
apps/barcode/README.md Normal file
View File

@ -0,0 +1,23 @@
# Barcode clockwatchface
A scannable EAN-8 compatible clockwatchface for your Bangle 2
The format of the bars are
`||HHmm||MMwc||`
* Left section: HHmm
* H: Hours
* m: Minutes
* Right section: MM9c
* M: Day of month
* w: Day of week
* c: Calculated EAN-8 digit checksum
Apart from that
* The upper left section displays total number of steps per day
* The upper right section displays total number of steps from last boot ("stepuptime")
* The face updates every 5 minutes or on demant by pressing the hardware button
This clockwathface is aware of theme choice, so it will adapt to Light/Dark themes.

428
apps/barcode/barcode.app.js Normal file
View File

@ -0,0 +1,428 @@
/* Sizes */
let checkBarWidth = 10;
let checkBarHeight = 140;
let digitBarWidth = 14;
let digitBarHeight = 100;
let textBarWidth = 56;
let textBarHeight = 20;
let textWidth = 14;
let textHeight = 20;
/* Offsets */
var startOffsetX = 17;
var startOffsetY = 30;
let startBarOffsetX = startOffsetX;
let startBarOffsetY = startOffsetY;
let upperTextBarLeftOffsetX = startBarOffsetX + checkBarWidth;
let upperTextBarLeftOffsetY = startOffsetY;
let midBarOffsetX = upperTextBarLeftOffsetX + textBarWidth;
let midBarOffsetY = startOffsetY;
let upperTextBarRightOffsetX = midBarOffsetX + checkBarWidth;
let upperTextBarRightOffsetY = startOffsetY;
let endBarOffsetX = upperTextBarRightOffsetX + textBarWidth;
let endBarOffsetY = startOffsetY;
let leftBarsStartX = startBarOffsetX + checkBarWidth;
let leftBarsStartY = upperTextBarLeftOffsetY + textBarHeight;
let rightBarsStartX = midBarOffsetX + checkBarWidth;
let rightBarsStartY = upperTextBarRightOffsetY + textBarHeight;
/* Utilities */
let stepCount = require("Storage").readJSON("stepCount",1);
if(stepCount === undefined) stepCount = 0;
let intCaster = num => Number(num);
var drawTimeout;
function renderWatch(l) {
g.setFont("4x6",2);
// work out how to display the current time
var d = new Date();
var h = d.getHours(), m = d.getMinutes();
var time = h + ":" + ("0"+m).substr(-2);
//var month = ("0" + (d.getMonth()+1)).slice(-2);
var dayOfMonth = ('0' + d.getDate()).slice(-2);
var dayOfWeek = d.getDay() || 7;
var concatTime = ("0"+h).substr(-2) + ("0"+m).substr(-2) + dayOfMonth + dayOfWeek;
const chars = String(concatTime).split("").map((concatTime) => {
return Number(concatTime);
});
const checkSum = calculateChecksum(chars);
concatTime += checkSum;
drawCheckBar(startBarOffsetX, startBarOffsetY);
drawLDigit(chars[0], 0, leftBarsStartY);
drawLDigit(chars[1], 1, leftBarsStartY);
drawLDigit(chars[2], 2, leftBarsStartY);
drawLDigit(chars[3], 3, leftBarsStartY);
g.drawString(getStepCount(), startOffsetX + checkBarWidth + 3, startOffsetY + 4);
g.drawString(concatTime.substring(0,4), startOffsetX + checkBarWidth + 3, startOffsetY + textBarHeight + digitBarHeight + 6);
drawCheckBar(midBarOffsetX, midBarOffsetY);
drawRDigit(chars[4], 0, rightBarsStartY);
drawRDigit(chars[5], 1, rightBarsStartY);
drawRDigit(chars[6], 2, rightBarsStartY);
drawRDigit(checkSum, 3, rightBarsStartY);
g.drawString(Bangle.getStepCount(), midBarOffsetX + checkBarWidth + 3, startOffsetY + 4);
g.drawString(concatTime.substring(4), midBarOffsetX + checkBarWidth + 3, startOffsetY + textBarHeight + digitBarHeight + 6);
drawCheckBar(endBarOffsetX, endBarOffsetY);
// schedule a draw for the next minute
if (drawTimeout) {
clearTimeout(drawTimeout);
}
drawTimeout = setTimeout(function() {
drawTimeout = undefined;
layout.render(layout.watch);
}, (1000 * 60 * 5) - (Date.now() % (1000 * 60 * 5)));
}
function drawLDigit(digit, index, offsetY) {
switch(digit) {
case 0:
drawLZeroWithOffset(leftBarsStartX+(digitBarWidth*index), offsetY);
break;
case 1:
drawLOneWithOffset(leftBarsStartX+(digitBarWidth*index), offsetY);
break;
case 2:
drawLTwoWithOffset(leftBarsStartX+(digitBarWidth*index), offsetY);
break;
case 3:
drawLThreeWithOffset(leftBarsStartX+(digitBarWidth*index), offsetY);
break;
case 4:
drawLFourWithOffset(leftBarsStartX+(digitBarWidth*index), offsetY);
break;
case 5:
drawLFiveWithOffset(leftBarsStartX+(digitBarWidth*index), offsetY);
break;
case 6:
drawLSixWithOffset(leftBarsStartX+(digitBarWidth*index), offsetY);
break;
case 7:
drawLSevenWithOffset(leftBarsStartX+(digitBarWidth*index), offsetY);
break;
case 8:
drawLEightWithOffset(leftBarsStartX+(digitBarWidth*index), offsetY);
break;
case 9:
drawLNineWithOffset(leftBarsStartX+(digitBarWidth*index), offsetY);
break;
}
}
function drawRDigit(digit, index, offsetY) {
switch(digit) {
case 0:
drawRZeroWithOffset(rightBarsStartX+(digitBarWidth*index), offsetY);
break;
case 1:
drawROneWithOffset(rightBarsStartX+(digitBarWidth*index), offsetY);
break;
case 2:
drawRTwoWithOffset(rightBarsStartX+(digitBarWidth*index), offsetY);
break;
case 3:
drawRThreeWithOffset(rightBarsStartX+(digitBarWidth*index), offsetY);
break;
case 4:
drawRFourWithOffset(rightBarsStartX+(digitBarWidth*index), offsetY);
break;
case 5:
drawRFiveWithOffset(rightBarsStartX+(digitBarWidth*index), offsetY);
break;
case 6:
drawRSixWithOffset(rightBarsStartX+(digitBarWidth*index), offsetY);
break;
case 7:
drawRSevenWithOffset(rightBarsStartX+(digitBarWidth*index), offsetY);
break;
case 8:
drawREightWithOffset(rightBarsStartX+(digitBarWidth*index), offsetY);
break;
case 9:
drawRNineWithOffset(rightBarsStartX+(digitBarWidth*index), offsetY);
break;
}
}
/*
LEAN
01234567890123
xxxx xx
xx xxxx
xxxxxxxx xx
xx xxxx
xxxx xx
xx xxxxxxxx
xxxxxx xxxx
xxxx xxxxxx
xx xxxx
xxxx xx
*/
function drawLOneWithOffset(offset, offsetY) {
let barOneX = 4;
let barTwoX = 12;
g.fillRect(barOneX+offset,offsetY+0,barOneX+3+offset,offsetY+digitBarHeight);
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+1+offset,offsetY+digitBarHeight);
//g.drawString("1",offset+3,offsetY+digitHeight+5);
}
function drawLTwoWithOffset(offset, offsetY) {
let barOneX = 4;
let barTwoX = 10;
g.fillRect(barOneX+offset,offsetY+0,barOneX+1+offset,offsetY+digitBarHeight);
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+3+offset,offsetY+digitBarHeight);
//g.drawString("2",offset+3,offsetY+digitHeight+5);
}
function drawLThreeWithOffset(offset, offsetY) {
let barOneX = 2;
let barTwoX = 12;
g.fillRect(barOneX+offset,offsetY+0,barOneX+7+offset,offsetY+digitBarHeight);
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+1+offset,offsetY+digitBarHeight);
//g.drawString("3",offset+3,offsetY+digitHeight+5);
}
function drawLFourWithOffset(offset, offsetY) {
let barOneX = 2;
let barTwoX = 10;
g.fillRect(barOneX+offset,offsetY+0,barOneX+1+offset,offsetY+digitBarHeight);
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+3+offset,offsetY+digitBarHeight);
//g.drawString("4",offset+3,offsetY+digitHeight+5);
}
function drawLFiveWithOffset(offset, offsetY) {
let barOneX = 2;
let barTwoX = 12;
g.fillRect(barOneX+offset,offsetY+0,barOneX+3+offset,offsetY+digitBarHeight);
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+1+offset,offsetY+digitBarHeight);
//g.drawString("5",offset+3,offsetY+digitHeight+5);
}
function drawLSixWithOffset(offset, offsetY) {
let barOneX = 2;
let barTwoX = 6;
g.fillRect(barOneX+offset,offsetY+0,barOneX+1+offset,offsetY+digitBarHeight);
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+7+offset,offsetY+digitBarHeight);
//g.drawString("6",offset+3,offsetY+digitHeight+5);
}
function drawLSevenWithOffset(offset, offsetY) {
let barOneX = 2;
let barTwoX = 10;
g.fillRect(barOneX+offset,offsetY+0,barOneX+5+offset,offsetY+digitBarHeight);
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+3+offset,offsetY+digitBarHeight);
//g.drawString("7",offset+3,offsetY+digitHeight+5);
}
function drawLEightWithOffset(offset, offsetY) {
let barOneX = 2;
let barTwoX = 8;
g.fillRect(barOneX+offset,offsetY+0,barOneX+3+offset,offsetY+digitBarHeight);
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+5+offset,offsetY+digitBarHeight);
//g.drawString("8",offset+3,offsetY+digitHeight+5);
}
function drawLNineWithOffset(offset, offsetY) {
let barOneX = 6;
let barTwoX = 10;
g.fillRect(barOneX+offset,offsetY+0,barOneX+1+offset,offsetY+digitBarHeight);
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+3+offset,offsetY+digitBarHeight);
//g.drawString("9",offset+3,offsetY+digitHeight+5);
}
function drawLZeroWithOffset(offset, offsetY) {
let barOneX = 6;
let barTwoX = 12;
g.fillRect(barOneX+offset,offsetY+0,barOneX+3+offset,offsetY+digitBarHeight);
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+1+offset,offsetY+digitBarHeight);
//g.drawString("0",offset+3,offsetY+digitHeight+5);
}
/*
REAN
01234567890123
xxxx xxxx
xxxx xxxx
xx xx
xx xxxxxx
xx xxxxxx
xx xx
xx xx
xx xx
xxxxxx xx
xxxxxx xx
*/
function drawROneWithOffset(offset, offsetY) {
let barOneX = 0;
let barTwoX = 8;
g.fillRect(offset+barOneX,offsetY+0,offset+barOneX+3,offsetY+digitBarHeight);
g.fillRect(offset+barTwoX,offsetY+0,offset+barTwoX+3,offsetY+digitBarHeight);
//g.drawString("1",offset+2,offsetY+textHeight+5);
}
function drawRTwoWithOffset(offset, offsetY) {
let barOneX = 0;
let barTwoX = 6;
g.fillRect(offset+barOneX,offsetY+0,offset+barOneX+3,offsetY+digitBarHeight);
g.fillRect(offset+barTwoX,offsetY+0,offset+barTwoX+3,offsetY+digitBarHeight);
//g.drawString("2",offset+2,offsetY+textHeight+5);
}
function drawRThreeWithOffset(offset, offsetY) {
let barOneX = 0;
let barTwoX = 10;
g.fillRect(barOneX+offset,offsetY+0,barOneX+1+offset,offsetY+digitBarHeight);
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+1+offset,offsetY+digitBarHeight);
//g.drawString("3",offset+2,offsetY+textHeight+5);
}
function drawRFourWithOffset(offset, offsetY) {
let barOneX = 0;
let barTwoX = 4;
g.fillRect(barOneX+offset,offsetY+0,barOneX+1+offset,offsetY+digitBarHeight);
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+5+offset,offsetY+digitBarHeight);
//g.drawString("4",offset+2,offsetY+textHeight+5);
}
function drawRFiveWithOffset(offset, offsetY) {
let barOneX = 0;
let barTwoX = 6;
g.fillRect(barOneX+offset,offsetY+0,barOneX+1+offset,offsetY+digitBarHeight);
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+5+offset,offsetY+digitBarHeight);
//g.drawString("5",offset+2,offsetY+textHeight+5);
}
function drawRSixWithOffset(offset, offsetY) {
let barOneX = 0;
let barTwoX = 4;
g.fillRect(barOneX+offset,offsetY+0,barOneX+1+offset,offsetY+digitBarHeight);
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+1+offset,offsetY+digitBarHeight);
//g.drawString("6",offset+2,offsetY+textHeight+5);
}
function drawRSevenWithOffset(offset, offsetY) {
let barOneX = 0;
let barTwoX = 8;
g.fillRect(barOneX+offset,offsetY+0,barOneX+1+offset,offsetY+digitBarHeight);
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+1+offset,offsetY+digitBarHeight);
//g.drawString("7",offset+2,offsetY+textHeight+5);
}
function drawREightWithOffset(offset, offsetY) {
let barOneX = 0;
let barTwoX = 6;
g.fillRect(offset+barOneX,offsetY+0,offset+barOneX+1,offsetY+digitBarHeight);
g.fillRect(offset+barTwoX,offsetY+0,offset+barTwoX+1,offsetY+digitBarHeight);
//g.drawString("8",offset+2,offsetY+textHeight+5);
}
function drawRNineWithOffset(offset, offsetY) {
let barOneX = 0;
let barTwoX = 8;
g.fillRect(barOneX+offset,offsetY+0,barOneX+5+offset,offsetY+digitBarHeight);
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+1+offset,offsetY+digitBarHeight);
//g.drawString("9",offset+2,offsetY+textHeight+5);
}
function drawRZeroWithOffset(offset, offsetY) {
let barOneX = 0;
let barTwoX = 10;
g.fillRect(barOneX+offset,offsetY+0,barOneX+5+offset,offsetY+digitBarHeight);
g.fillRect(barTwoX+offset,offsetY+0,barTwoX+1+offset,offsetY+digitBarHeight);
//g.drawString("0",offset+2,offsetY+textHeight+5);
}
function drawCheckBar(offsetX, offsetY) {
const barOneX = offsetX+2;
const barOneWidth = 1;
const barTwoX = offsetX+6;
const barTwoWidth = 1;
g.fillRect(barOneX,offsetY,barOneX+barOneWidth,offsetY+checkBarHeight);
g.fillRect(barTwoX,offsetY,barTwoX+barTwoWidth,offsetY+checkBarHeight);
}
function calculateChecksum(digits) {
let oddSum = digits[6] + digits[4] + digits[2] + digits[0];
let evenSum = digits[5] + digits[3] + digits[1];
let checkSum = (10 - ((3 * oddSum + evenSum) % 10)) % 10;
return checkSum;
}
function storeStepCount() {
stepCount = Bangle.getStepCount();
require("Storage").writeJSON("stepCount",stepCount);
}
function getStepCount() {
let accumulatedSteps = Bangle.getStepCount();
if(accumulatedSteps <= stepCount) {
return 0;
}
return accumulatedSteps - stepCount;
}
function resetAtMidnight() {
let now = new Date();
let night = new Date(
now.getFullYear(),
now.getMonth(),
now.getDate(), // the next day, ...
23, 58, 0 // ...at 00:00:00 hours
);
let msToMidnight = night.getTime() - now.getTime();
setTimeout(function() {
storeStepCount(); // <-- This is the function being called at midnight.
resetAtMidnight(); // Then, reset again next midnight.
}, msToMidnight);
}
resetAtMidnight();
// The layout, referencing the custom renderer
var Layout = require("Layout");
var layout = new Layout( {
type:"v", c: [
{type:"custom", render:renderWatch, id:"watch", bgCol:g.theme.bg, fillx:1, filly:1 }
]
});
// Clear the screen once, at startup
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
Bangle.setUI("clock");
layout.render();
Bangle.on('lock', function(locked) {
if(!locked) {
layout.render();
}
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1 @@
E.toArrayBuffer(atob("MDAE///+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifnD6khY+bTIiDTIn8gWq8n+if/////+ifrV6mlp+rXYiFXZr8k4q8r+if/////+if7+///u//79ie7/7//u///+if/////+ifnP6t+378r+ienvyf/K/7v+if/////+ifrfx6/I78j+ibe/+e/W75n+if/////+iee/+t/Z77f+iervyv/r/7v+if//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////"))

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,16 @@
{ "id": "barcode",
"name": "Barcode clock",
"shortName":"Barcode clock",
"icon": "barcode.icon.png",
"version":"0.09",
"description": "EAN-8 compatible barcode clock.",
"tags": "barcode,ean,ean-8,watchface,clock,clockface",
"type": "clock",
"supports" : ["BANGLEJS2"],
"storage": [
{"name":"barcode.app.js","url":"barcode.app.js"},
{"name":"barcode.img","url":"barcode.icon.js","evaluate":true}
],
"readme":"README.md",
"screenshots": [{"url":"barcode.clock.png"}]
}

5
apps/bigdclock/ChangeLog Normal file
View File

@ -0,0 +1,5 @@
0.01: Initial version
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.04: bug fix
0.05: proper fix for the race condition in queueDraw()

14
apps/bigdclock/README.md Normal file
View File

@ -0,0 +1,14 @@
# Big Digit Clock
There are a number of big digit clocks available for the Bangle, but this is
the first which shows all the essential information that a clock needs to show
in a manner that is easy to read by those with poor eyesight.
The clock shows the time-of-day, the day-of-week and the day-of-month, as well
as an easy-to-see icon showing the current charge on the battery.
![screenshot](./screenshot.png)
## Creator
Created by [Deirdre O'Byrne](https://github.com/deirdreobyrne)

View File

@ -0,0 +1,91 @@
// <link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@600&display=swap" rel="stylesheet">
Graphics.prototype.setFontOpenSans = function(scale) {
// Actual height 48 (50 - 3)
this.setFontCustom(
atob('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAAAAAAAP8AAAAAAAAB/wAAAAAAAAH/gAAAAAAAAf+AAAAAAAAB/4AAAAAAAAH/gAAAAAAAAf8AAAAAAAAA/wAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAPAAAAAAAAAH8AAAAAAAAD/wAAAAAAAA//AAAAAAAAf/8AAAAAAAP//wAAAAAAH///AAAAAAB///4AAAAAA///8AAAAAAf///AAAAAAH///gAAAAAD///wAAAAAB///4AAAAAAf//+AAAAAAP///AAAAAAH///gAAAAAA///4AAAAAAD//8AAAAAAAP/+AAAAAAAA//gAAAAAAAD/wAAAAAAAAP4AAAAAAAAA8AAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/gAAAAAAAP///AAAAAAH////gAAAAD/////gAAAAf/////gAAAH//////AAAA//////+AAAD//////8AAAf//////4AAD//4AH//gAAP/gAAAf/AAA/4AAAAf8AAH/AAAAA/4AAf4AAAAB/gAB/AAAAAD+AAH8AAAAAP4AAfwAAAAA/gAB/AAAAAD+AAH8AAAAAP4AAf4AAAAB/gAB/gAAAAH+AAD/gAAAB/wAAP/gAAAf/AAA//4AAf/8AAB///////gAAD//////8AAAH//////wAAAP/////+AAAAf/////wAAAA/////8AAAAAf////AAAAAAf///gAAAAAAB//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAAHgAAAAAAAAA/AAAAAAAAAH+AAAAAAAAA/4AAAAAAAAD/AAAAAAAAAf8AAAAAAAAD/gAAAAAAAAf8AAAAAAAAB/gAAAAAAAAP8AAAAAAAAB/wAAAAAAAAP///////AAA///////8AAD///////wAAP///////AAA///////8AAD///////wAAP///////AAA///////8AAD///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAD8AAAOAAAAAfwAAB+AAAAD/AAAH8AAAAf8AAA/wAAAH/wAAH/AAAA//AAAf4AAAH/8AAD/gAAA//wAAP8AAAH//AAA/gAAA//8AAD+AAAH//wAAf4AAA/9/AAB/AAAH/n8AAH8AAA/8fwAAfwAAH/h/AAB/AAA/8H8AAH8AAH/gfwAAfwAA/8B/AAB/gAH/gH8AAH+AB/8AfwAAP8Af/gB/AAA////8AH8AAD////gAfwAAH///8AB/AAAf///gAH8AAA///8AAfwAAB///gAB/AAAD//4AAH8AAAH/+AAAfwAAAH/gAAB/AAAAAAAAAH8AAAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwAAAYAAAAB/gAAB4AAAAD+AAAPwAAAAP4AAA/gAAAAfwAAH+AAAAB/AAAf4AAAAH8AAD/AB+AAfwAAP8AH4AA/gAA/gAfgAD+AAH+AB+AAP4AAfwAH4AA/gAB/AAfgAD+AAH8AB+AAP4AAfwAH4AA/gAB/AA/wAD+AAH8AD/AAP4AAfwAP8AA/gAB/AA/wAD+AAH+AH/AAf4AAf4Af+AB/AAA/wH/8AP8AAD////4D/wAAP//+////AAAf//7///4AAB///P///gAAD//8f//8AAAP//h///gAAAf/8D//+AAAA//gH//wAAAA/4AP/8AAAAAAAAP/AAAAAAAAAHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8AAAAAAAAA/wAAAAAAAAH/AAAAAAAAB/8AAAAAAAAP/wAAAAAAAD//AAAAAAAAf/8AAAAAAAH//wAAAAAAA/+/AAAAAAAP/z8AAAAAAB/8PwAAAAAAf/g/AAAAAAD/4D8AAAAAAf/APwAAAAAH/4A/AAAAAA/+AD8AAAAAP/wAPwAAAAB/8AA/AAAAAf/gAD8AAAAD/4AAPwAAAA//AAA/AAAAD///////wAAP///////AAA///////8AAD///////wAAP///////AAA///////8AAD///////wAAP///////AAAAAAAA/AAAAAAAAAD8AAAAAAAAAPwAAAAAAAAA/AAAAAAAAAD8AAAAAAAAAPwAAAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwAAAAAAQAB/gAAAD//gAD+AAA////AAP8AAD///8AAfwAAP///wAB/AAA////AAH8AAD///8AAf4AAP///wAA/gAA////AAD+AAD/+H8AAP4AAP4AfwAA/gAA/gB+AAD+AAD+AH4AAP4AAP4AfwAA/gAA/gB/AAD+AAD+AH8AAP4AAP4AfwAB/gAA/gB/AAH+AAD+AH+AA/wAAP4Af8AD/AAA/gB/4A/8AAD+AD////gAAP4AP///+AAA/gAf///wAAD+AB////AAAP4AD///4AAA/gAH///AAAAAAAP//4AAAAAAAf/+AAAAAAAAf/gAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//AAAAAAAf///gAAAAAP////gAAAAD/////gAAAAf/////AAAAD/////+AAAA//////8AAAD//////4AAAf/8/4//gAAD/8H+Af/AAAP/AfgAf8AAB/wD+AA/wAAH+APwAB/gAA/wB/AAD+AAD+AH4AAP4AAP4AfgAA/gAB/gB+AAD+AAH8AH4AAP4AAfwAfgAA/gAB/AB/AAH+AAH8AH8AAf4AAfwAf4AD/AAB/AB/4A/8AAH8AH////wAAfwAP///+AAB/AA////4AAH8AB////AAAfwAD///4AAA/AAH///AAAAAAAP//4AAAAAAAP/+AAAAAAAAP/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP4AAAAAAAAA/gAAAAAAAAD+AAAAAAAAAP4AAAAAAAAA/gAAAAAAAAD+AAAAAAAAAP4AAAAADAAA/gAAAAB8AAD+AAAAAfwAAP4AAAAH/AAA/gAAAB/8AAD+AAAAf/wAAP4AAAP//AAA/gAAD//8AAD+AAA///wAAP4AAP//8AAA/gAD///AAAD+AB///wAAAP4Af//4AAAA/gH//+AAAAD+B///gAAAAP4f//wAAAAA/v//8AAAAAD////AAAAAAP///wAAAAAA///4AAAAAAD//+AAAAAAAP//gAAAAAAA//wAAAAAAAD/8AAAAAAAAP/AAAAAAAAA/wAAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAAAAAAAAAH/wAAAAH8AB//gAAAB/8AP//gAAAf/8B//+AAAB//4P//8AAAP//w///4AAB///n///gAAH//+////AAA/////gf8AAD/h//4AfwAAP4B//AB/gAB/gD/4AD+AAH8AP/gAP4AAfwAf8AA/gAB/AA/wAB+AAH4AD/AAH4AAfwAP8AAfgAB/AB/4AD+AAH8AH/gAP4AAfwA//AA/gAA/gH/+AH+AAD/h//8AfwAAP//+/4D/AAAf//7///8AAB///H///gAAD//4P//+AAAP//g///wAAAf/8B//+AAAA//gD//wAAAA/4AH/+AAAAAAAAH/wAAAAAAAAH4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8AAAAAAAAD//AAAAAAAA///AAAAAAAH//+AAPwAAB///8AA/gAAP///4AD+AAA////wAP4AAH////AA/gAAf///8AD+AAD/4B/4AP4AAP+AB/gA/gAA/gAD+AD+AAH+AAP4AP4AAfwAAfgA/gAB/AAB+AD+AAH8AAH4AP4AAfwAAfgB/AAB/AAB+AH8AAH8AAH4A/wAAf4AA/AH+AAA/gAD8A/4AAD/gAfwH/gAAP/AD+B/8AAAf/g/w//gAAB//////+AAAD//////wAAAH/////+AAAAP/////wAAAAf////8AAAAA/////AAAAAA////wAAAAAAf//4AAAAAAAH/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAfgAAAAP8AAD/AAAAA/4AAf8AAAAH/gAB/4AAAAf+AAH/gAAAB/4AAf+AAAAH/gAB/4AAAAf+AAH/AAAAA/wAAP8AAAAB+AAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=='),
46,
atob("EhklJSUlJSUlJSUlEg=="),
64+(scale<<8)+(1<<16)
);
};
var drawTimeout;
function queueDraw(millis_now) {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function () {
drawTimeout = undefined;
draw();
}, 60000 - (millis_now % 60000));
}
function draw() {
var date = new Date();
var h = date.getHours(),
m = date.getMinutes();
var d = date.getDate(),
w = date.getDay(); // d=1..31; w=0..6
const level = E.getBattery();
const width = level + (level/2);
var is12Hour = (require("Storage").readJSON("setting.json", 1) || {})["12hour"];
var dows = require("date_utils").dows(0,1);
g.reset();
g.clear();
g.setFontOpenSans();
g.setFontAlign(0, -1);
if (is12Hour) {
if (h > 12) h -= 12;
if (h == 0) h = 12;
g.drawString(h + ":" + ("0"+m).substr(-2), g.getWidth() / 2, 30);
} else {
g.drawString(("0"+h).substr(-2) + ":" + ("0"+m).substr(-2), g.getWidth() / 2, 30);
}
g.setFontAlign(1, -1);
g.drawString(d, g.getWidth() -6, 98);
g.setFont('Vector', 52);
g.setFontAlign(-1, -1);
g.drawString(dows[w].slice(0,2).toUpperCase(), 6, 103);
g.fillRect(9,159,166,171);
g.fillRect(167,163,170,167);
if (Bangle.isCharging()) {
g.setColor(1,1,0);
} else if (level > 40) {
g.setColor(0,1,0);
} else {
g.setColor(1,0,0);
}
g.fillRect(12,162,12+width,168);
if (level < 100) {
g.setColor(g.theme.bg);
g.fillRect(12+width+1,162,162,168);
}
g.setColor(0, 1, 0);
g.fillRect(0, 90, g.getWidth(), 94);
// widget redraw
Bangle.drawWidgets();
queueDraw(date.getTime());
}
Bangle.on('lcdPower', on => {
if (on) {
draw(); // draw immediately, queue redraw
} else { // stop draw timer
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
}
});
Bangle.on('charging', (charging) => {
draw();
});
Bangle.loadWidgets();
draw();
Bangle.setUI("clock");

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwgJC/AAMD4F4AgN4g/D/4FB/E/AoUH/F/AoOAh4FCz4FD4EPAoUHAoOHwAFDx/AAoUfAol/g4RD/w1Cg/B/AFD4fwn4XC4fg8/wAoPH//P7AFE9wFE8YFEEwcf4+BwAFBiACBAoUwAQPAAQMgAQNAArIjFF4sYgEBAoUIAoIRChi3B8AFBg8Ah/wAoIVBjH8ZAXguF+AoSDBn7WEh4FEg4"))

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@ -0,0 +1,17 @@
{ "id": "bigdclock",
"name": "Big digit clock containing just the essentials",
"shortName":"Big digit clk",
"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.",
"icon": "bigdclock.png",
"type": "clock",
"tags": "clock",
"allow_emulator":true,
"supports" : ["BANGLEJS2"],
"readme": "README.md",
"screenshots": [ { "url":"screenshot.png" } ],
"storage": [
{"name":"bigdclock.app.js","url":"bigdclock.app.js"},
{"name":"bigdclock.img","url":"bigdclock.icon.js","evaluate":true}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -7,3 +7,4 @@
0.07: Fix off-by-one-error on previous month
0.08: Do not register as watch, manually start clock on button
read start of week from system settings
0.09: Fix scope of let variables

View File

@ -16,6 +16,12 @@ const white = "#ffffff";
const red = "#d41706";
const blue = "#0000ff";
const yellow = "#ffff00";
let bgColor = color4;
let bgColorMonth = color1;
let bgColorDow = color2;
let bgColorWeekend = color3;
let fgOtherMonth = gray1;
let fgSameMonth = white;
let settings = require('Storage').readJSON("calendar.json", true) || {};
let startOnSun = ((require("Storage").readJSON("setting.json", true) || {}).firstDayOfWeek || 0) === 0;
@ -27,19 +33,12 @@ if (settings.ndColors === undefined)
}
if (settings.ndColors === true) {
let bgColor = white;
let bgColorMonth = blue;
let bgColorDow = black;
let bgColorWeekend = yellow;
let fgOtherMonth = blue;
let fgSameMonth = black;
} else {
let bgColor = color4;
let bgColorMonth = color1;
let bgColorDow = color2;
let bgColorWeekend = color3;
let fgOtherMonth = gray1;
let fgSameMonth = white;
bgColor = white;
bgColorMonth = blue;
bgColorDow = black;
bgColorWeekend = yellow;
fgOtherMonth = blue;
fgSameMonth = black;
}
function getDowLbls(locale) {

View File

@ -1,7 +1,7 @@
{
"id": "calendar",
"name": "Calendar",
"version": "0.08",
"version": "0.09",
"description": "Simple calendar",
"icon": "calendar.png",
"screenshots": [{"url":"screenshot_calendar.png"}],

View File

@ -0,0 +1,2 @@
1.00: New App!
1.01: Use fractional numbers and scale the points to keep working consistently on whole screen

View File

@ -6,6 +6,7 @@ A simple calibration app for the touchscreen
Once lauched touch the cross that appear on the screen to make
another spawn elsewhere.
each new touch on the screen will help to calibrate the offset
of your finger on the screen. After five or more input, press
the button to save the calibration and close the application.
Each new touch on the screen will help to calibrate the offset
of your finger on the screen. After four or more inputs, press
the button to save the calibration and close the application. Quality
of the calibration gets better with every touch on a cross.

View File

@ -1,40 +1,60 @@
class BanglejsApp {
constructor() {
this.maxSamples = 16;
this.target = {
xMin: Math.floor(0.1 * g.getWidth()),
xMax: Math.floor(0.9 * g.getWidth()),
yMin: Math.floor(0.1 * g.getHeight()),
yMax: Math.floor(0.9 * g.getHeight()),
};
this.x = 0;
this.y = 0;
this.step = 0;
this.settings = {
xoffset: 0,
yoffset: 0,
xoffset: [0],
yoffset: [0],
xMaxActual: [this.target.xMax],
yMaxActual: [this.target.yMax],
};
}
load_settings() {
let settings = require('Storage').readJSON('calibration.json', true) || {active: false};
// do nothing if the calibration is deactivated
if (settings.active === true) {
// cancel the calibration offset
Bangle.on('touch', function(button, xy) {
xy.x += settings.xoffset;
xy.y += settings.yoffset;
});
}
if (!settings.xoffset) settings.xoffset = 0;
if (!settings.yoffset) settings.yoffset = 0;
console.log('loaded settings:');
console.log(settings);
return settings;
}
save_settings() {
this.settings.active = true;
this.settings.reload = false;
require('Storage').writeJSON('calibration.json', this.settings);
getMedian(array){
array.sort();
let i = Math.floor(array.length/2);
if ( array.length % 2 && array.length > 1 ){
return (array[i]+array[i+1])/2;
} else {
return array[i];
}
}
console.log('saved settings:');
console.log(this.settings);
getMedianSettings(){
let medianSettings = {
xoffset: this.getMedian(this.settings.xoffset),
yoffset: this.getMedian(this.settings.yoffset)
};
medianSettings.xscale = this.target.xMax / (medianSettings.xoffset + this.getMedian(this.settings.xMaxActual));
medianSettings.yscale = this.target.yMax / (medianSettings.yoffset + this.getMedian(this.settings.yMaxActual));
return medianSettings;
}
save_settings() {
let settingsToSave = this.getMedianSettings();
settingsToSave.active = true;
settingsToSave.reload = false;
require('Storage').writeJSON('calibration.json', settingsToSave);
console.log('saved settings:', settingsToSave);
}
explain() {
@ -46,29 +66,78 @@ class BanglejsApp {
}
drawTarget() {
this.x = 16 + Math.floor(Math.random() * (g.getWidth() - 32));
this.y = 40 + Math.floor(Math.random() * (g.getHeight() - 80));
switch (this.step){
case 0:
this.x = this.target.xMin;
this.y = this.target.yMin;
break;
case 1:
this.x = this.target.xMax;
this.y = this.target.yMin;
break;
case 2:
this.x = this.target.xMin;
this.y = this.target.yMax;
break;
case 3:
this.x = this.target.xMax;
this.y = this.target.yMax;
break;
}
g.clearRect(0, 24, g.getWidth(), g.getHeight() - 24);
g.clearRect(0, 0, g.getWidth(), g.getHeight());
g.setColor(g.theme.fg);
g.drawLine(this.x, this.y - 5, this.x, this.y + 5);
g.drawLine(this.x - 5, this.y, this.x + 5, this.y);
g.setFont('Vector', 10);
g.drawString('current offset: ' + this.settings.xoffset + ', ' + this.settings.yoffset, 0, 24);
let medianSettings = this.getMedianSettings();
g.drawString('current offset: ' + medianSettings.xoffset.toFixed(3) + ', ' + medianSettings.yoffset.toFixed(3), 2, (g.getHeight()/2)-6);
g.drawString('current scale: ' + medianSettings.xscale.toFixed(3) + ', ' + medianSettings.yscale.toFixed(3), 2, (g.getHeight()/2)+6);
}
setOffset(xy) {
this.settings.xoffset = Math.round((this.settings.xoffset + (this.x - Math.floor((this.x + xy.x)/2)))/2);
this.settings.yoffset = Math.round((this.settings.yoffset + (this.y - Math.floor((this.y + xy.y)/2)))/2);
switch (this.step){
case 0:
this.settings.xoffset.push(this.x - xy.x);
this.settings.yoffset.push(this.y - xy.y);
break;
case 1:
this.settings.xMaxActual.push(xy.x);
this.settings.yoffset.push(this.y - xy.y);
break;
case 2:
this.settings.xoffset.push(this.x - xy.x);
this.settings.yMaxActual.push(xy.y);
break;
case 3:
this.settings.xMaxActual.push(xy.x);
this.settings.yMaxActual.push(xy.y);
break;
}
for (let c in this.settings){
if (this.settings[c].length > this.maxSamples) this.settings[c] = this.settings[c].slice(1, this.maxSamples);
}
}
nextStep() {
this.step++;
if ( this.step == 4 ) this.step = 0;
}
}
E.srand(Date.now());
Bangle.loadWidgets();
Bangle.drawWidgets();
calibration = new BanglejsApp();
calibration.load_settings();
Bangle.disableCalibration = true;
function touchHandler (btn, xy){
if (xy) calibration.setOffset(xy);
calibration.nextStep();
calibration.drawTarget();
}
let modes = {
mode : 'custom',
@ -76,10 +145,7 @@ let modes = {
calibration.save_settings(this.settings);
load();
},
touch : function(btn, xy) {
calibration.setOffset(xy);
calibration.drawTarget();
},
touch : touchHandler,
};
Bangle.setUI(modes);
calibration.drawTarget();

View File

@ -1,7 +1,7 @@
let cal_settings = require('Storage').readJSON("calibration.json", true) || {active: false};
Bangle.on('touch', function(button, xy) {
// do nothing if the calibration is deactivated
if (cal_settings.active === false) return;
if (cal_settings.active === false || Bangle.disableCalibration) return;
// reload the calibration offset at each touch event /!\ bad for the flash memory
if (cal_settings.reload === true) {
@ -9,6 +9,6 @@ Bangle.on('touch', function(button, xy) {
}
// apply the calibration offset
xy.x += cal_settings.xoffset;
xy.y += cal_settings.yoffset;
xy.x = E.clip(Math.round((xy.x + (cal_settings.xoffset || 0)) * (cal_settings.xscale || 1)),0,g.getWidth());
xy.y = E.clip(Math.round((xy.y + (cal_settings.yoffset || 0)) * (cal_settings.yscale || 1)),0,g.getHeight());
});

View File

@ -2,7 +2,7 @@
"name": "Touchscreen Calibration",
"shortName":"Calibration",
"icon": "calibration.png",
"version":"1.00",
"version":"1.01",
"description": "A simple calibration app for the touchscreen",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",

View File

@ -0,0 +1,11 @@
0.0: Main App.
0.1: Performance Fixes.
0.2: Correct Screen Clear and Draw.
0.3: Minor Fixes.
0.4: Clear Old Time on Screen Before Draw new Time
0.5: Update Date and Time to use Native date Funcions
0.6: Add Settings Page
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.9: Remove ESLint spaces
0.10: Show daily steps, heartrate and the temperature if weather information is available.

View File

@ -0,0 +1,11 @@
# cassioWatch
![Screenshot](screens/screen_night.png) ![Screenshot](screens/screen_day.png)
Clock with Space Cassio Watch Style.
It displays current temperature,day,steps,battery.heartbeat and weather.
**To-do**:
Align and change size of some elements.

175
apps/cassioWatch/app.js Normal file
View File

@ -0,0 +1,175 @@
const storage = require('Storage');
require("Font6x12").add(Graphics);
require("Font8x12").add(Graphics);
require("Font7x11Numeric7Seg").add(Graphics);
function bigThenSmall(big, small, x, y) {
g.setFont("7x11Numeric7Seg", 2);
g.drawString(big, x, y);
x += g.stringWidth(big);
g.setFont("8x12");
g.drawString(small, x, y);
}
function getBackgroundImage() {
return require("heatshrink").decompress(atob("2GwwkGIf4AfgMRkUiiIHCiMRiAMDAwYCCBAYVDAHMv/4ACkBIBAgPxBgM/BYXyAoICBCowA5gRADKQUDKAYMCmYCBiBXBCo4A5J4MxiMSKQUf+YBBBgSiBgc/kBXBBAMyCoK2CK/btCiUhfAJLCkBkDiMQgBXDCoUvNAJX+AAU/+MB/8wAQIAC+cQK5hoDgIEBBIQFEAYIPHBIgBBAQQIDBwZXSKIMxgJaBgEjmZYCmBXLgLBBkkAgUhiMxBIM0iMSCoMRkZECkQJEichBINDiETAgISBiQTDK6MvJAXzVIQrBBYMCK5E/K4kwGIJXFgdAMgQQBiYiCDgU0HQSlCgMikIEBEAMTDYJXQ+UikYDBj6nCAAMTWoJ6BK4oVEK4c0oQ+BK4MjAgMDJoJXHNYJXHBwa0BohcDY4QAKgJQE+LzBNwJVBkQMEkBXBCoyvFJAVAKISaBiMiHQRIDkVBoSyCK5CvBAgavNDAJAC+cQn5DCgSpBl4MDgBXBgCsBCoYoMLAKREgIKDBJIdKK5oA/AH4A/AH4A/ADUBIH4APiAFEi1mAGUADrkRKwUGK2ZXes1gK2xXfD8A3/K/4AWgxX/ACtga2AwIHLkAgCwvJw6RcDgIABK+w4cK/I4dsEGP5BXtSAQ6BV/5XSG4RX/K6Y3fK+42CK/5XTGwcGK/5XSVwY5cK+o1DAAayYsAhDsCv4K7BTBK4YeYK7CyFVzJXFFIpXtVwYiYK/rmZKYYDDELJXXG4YiaK/Y0aKgQAEK+gkdKt5XGKzqv5GTpX6ETlgK4xWrKTyxKVthXmAGRX/K/5X/AH5X/K/4gBAH4A/AFz/uAH4A/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/AH4A/AHNggEGHfEAgAEHKyQXVK0qTCAggbUK+6SDAApzXK/5BRDYZX3KxBBSYqxXngyvaV25XEd4ZCSsAcBAoRZ2dQZXBLwgaQCIYeCAGirCS4YGCDSJXCC6ZaodYICBZzSw4S4I+XDgSv4K4rzCK/47RAQTMaWHI9YV3TscV3aVagByBK3SwCSqyt8AAQ+XK/4A/AH4A/AH4A3gAA/AH4AuZbdggwc3ADpX/K/5XxsEAgA+XK/o8BgBX/K64/WK/4/XK/5X/K/5XvgBX/K64cYHrw4CSTFggCuXK4oDCEQJXYDS6ScDgg4CPKyRCAAZX0HAgBDK+LlYK4oeBAwZ9aK+lgAoQGBgyvzDIIDBK66sCG4JXYCwIBDK7ADCK+xZCHwJXzGoQ8BK7DpBAAaSXSgRXZO4okCK+IaXV4oABEILSWSYjRCHSo3BDSxXEAAIcBAISvyKawcIAYIGCK/4cUH4YlaHS0AHgI1XOg5YBPrY6WHgRXfAGRXDHzBX8VoJX/K68ADjRX6sBX/K/5X/K8wdcK/UAG7B0iKzZYbK/BWDAH4A/hWpzWhIf4ASgOpzIAB0EAhhH/AB8ZzGJ1WazMA4pH/AB+pxOZxOpzVMqA2ugUzmcgD7cKVYOqzGqpnRFw8ykchK8kviEBmQFBgMiFocSCAcSkUQAgMikRsHhWqxOq0Ut4mqBw0DC4IxBD4wpBHAQMCA4cCGJIAFj8hDIQuBkMTCwU/AYQJBiUxFoPxiIVDK4kyxUz4cxl+KK5MfDQXyD4UCmMSmAEBAQQHDgMTmIxHAAqpBmaqCFwMDEYZRBgEjCQQBB+USK5E/ns/0Uzwc6K48ykYkCK4IfCc4I4CK4QHEBAYAMiICBmYuDmQEBh8iAgRXCLISvJO4MqwcklEiK5CADV4oaBV4oHEK6Eve4JNCbwRfCiMTFoMDkMRSAJXCD49azWp0UqzWayJXIQwcAO4cCkMCFIJOCA4XxK6KPBkR6DTwYyBAwYPEAggfFzORpWK1OZyAOHJ4QfERAUSEgQxIIIgAr1URWIOZzOgGtwAhgMZzWq1OaIv4ASKgOqzTkvAEmq1WgFtQA=="));
}
function getRocketSequences() {
return {
1: require("heatshrink").decompress(atob("qFGwkCkQAiiEBEkUgKQhPhE8ogCE8YhCiQoEE7pKEPIgncTQ4neEwpQCPoh1eJYYwCJ7QmHKAh1hZIpOjPAUBJ0ZQCTzEhExZ1lPAZ1kKDQmOJ65O2E65OPOy5O2E64mPOyxO/J2wnPJyx2QJ35O/J2khE0p2POq52PEy4nOiQnlOrEhiSfMJrEggQnLJzB1CPBQmZkInMEzBQDPBImbPBR1ZEoRMCZYImhgQgEE0BzFKAgmaDwLDFKAbqdYQwHBOrcgDgLBFJrsiiRNGYbpLBY4Ymhd4omkkUhE0pQEEwUBJjrHBd4QmCdzoiBDwYrCPLyZHF4QnagQeCE8UgJwYniJwgnIOzwfFO0wJCJzMQE4gyFEzR2FBQombkInDQI4AakAnBTYS+ZE5BMDE0LEES7YnLE0R3FAEQA=")),
2: require("heatshrink").decompress(atob("qFGwkCkQAikMAgIliKYon/AA0gEAQniEwIhCAgYndEIjqBE8CaGKogmgKAp1fKAgncExBQBBQR1gKAp7BJ0IndExR4CE0idaOpYnbExqeYJxxPYEx0BJ0x2XExx2XJ20QE6xONJi5OPGwJOlBwLFkLoLFlBwJOkOwJOlE4JOkTjBOOE/52Pdi5OPEy7FnE5wmXE5xOZT5gmYEoMiiB1lgR4KTLAkDPBJ1WIAYDDKA4mWJwchDwYEDTjQiDJQh4GYLAhHFosSJy6OCTIxaEEywbBKYwjEEzMgUQxQFBogAURwZOGOjTKJdTYnOEryfHE0JQEfIpQgYQMAgJLeAgrtfTI4ndgSaFE4h0bdQkSZQpOfEAgIBO0AnEdrh2FJAb1EdbInEBIpObOwhOEEzYnFXzZ2HE4QlhE4QlDFMKcDYooniO0QnDT0YnCE0ciA")),
3: require("heatshrink").decompress(atob("qFGwkEogAjiMUEkVAKYgnhPYolgOQIniOYZ4FOcLqBE8CaGKojpgKAomhEYUQE7gmHKAIxCE0QkCPYR1gZIgnZExR4CJ0idmE7ZONYzImNgEUJ0p3YJRh2ZJJwnXOpQhBdkpaETsMEGQhOhE7jFLUYpOfTzgmKE4hOiE4hOigEUJ0rvCEywnPEqx2OTjBOOE7ImOTsqeZE5zFYoJOmT5kBJzEAih4LdK5mBAQInKOqoYDEgR4JEypHDEYbxJOq5ABdgZ7CEzZOEJQgnGihOYEIzJFTionCKYxWGEy9ADAYnGUIYmWog/EdBFAEy7KIKAwnjKwLqWE5pMeT48CVQpQfgMjKEtEiAnfEQJQCgJSCTcB6FJzkEdYcUE8FAdQghDOzonKTjh2EZAidcDoInHJzodBOwx/BE8JxcOwsAOwQmhJgSXDObwnFEwUUO0LFGE8aeiE4YmiokQE0tE")),
4: require("heatshrink").decompress(atob("qFGwkCkQAjiMSEkRTFE/4AGkMAgQCBE8MgEIYEDE7whDdQIngTQxVEE0ChFTjxQFE7jnFKAgxCOsBQFZgJ1gE7wmKPAROkTrTEHGAwnYiBHJFAaeXOoyXBEQZPac5AsFgJOhAoh2XJwwnFKoROdE4J9GJzwnIiQmVkInPAC0QE5AJFE64mHY5DFdE4SBEYr5JDJ0hKDJ0jCZJxoACgInmKLAmOTq5OOEy5OPTsxOYE5wmXO5wlYkAnMOqshiRNCgR4LOC8CkJCCEzxHDAgYnJOqpAEDoZ4HEyodDEQpQHdCsQOwwFHEyzoCPYzJGEy0gEwaZGA4acVEQSjHKAomXkQYEYAwlZeRKYDE8gjCYa7zJEwcCkImfKAb4FAD0hdTh4LgRSBOcR0CJz0gYYrrgN4QnEYrxOEE4bEeiAnGF4J2idL6VDE8ohBE0gnFE0J0BE4QGBiROgdIQABgJ2hJoTtjYgZSEE8ScgE4omikUQTcQADA=")),
5: require("heatshrink").decompress(atob("qFGwkCkQAikMAgIliKYonhiAnjkEATIIniEwIhCAgYndEIhQFYUZVEE0BQFOr5QEeQQmiKAL1DOr5QEE7ROCDgZVEAoInZDwchFQQoDPAJOdEQYrBdrZFDOYwncEJDsDVIpOXgJxEE4pObEAgGFgJOaE48BaIhOZJ5ZObY5ROcE441CE6xOGPAwtCJzpGCJ0hHDkI1DJzwoEJzInLFg52dUo5O/J35OzE54mWOx4mXJxx1XE54mXkUhExkSJzCfMOrAlBPBiZXgQDBAQQmgJgh4JOqoYEFYwmaDoZzEFgh1YDgkiiAFEKAroXJJAGFiQmVkCNDTIz5EJy57HKAomXkQYEJoqaYeRadEJrAnJEQUAgJPiAoYmeT4cCkAnBE0BKCJkT1EkDCeJYYiDOkLDFFL5wBE4guCPDhEBEwQiDY70CkInDiQnCJzkhOwhKDdzp2Idb4nEE0B0Bdo4niE0J0CeYhOhgESUYYnidsgnEE0KeCE0gnDE0ciA")),
6: require("heatshrink").decompress(atob("qFGwkCkQA/ABEgKQZPhEwgABEsAoGJkBxBE8JKEAowAbJIhQEgLDiPooAdKA4ncTZAndSwhQEFoInaJQkSKAwlZdgwnfSgYADE4h1ZDwInlcggnIOzAdCE8i7EY5J3XDgYhGd4pOZEI52bSYwGCOAJ2bYIodEOzZOFFAjFcEwwAIE6xOHABBO/J34ndEyx2PJ00BJ00SJ0p1XE54mXOxxO/J5wmYgQnMOrB2BPBgkWiJ1CPBbBYAYR4KiTAXRwIrFTjgZDJYZ4IEyoiEIwrDcEJJQFOqwiBDARxFFwgmXkAYDEogsBF4QmXEQJ7GUYYkBEzDKJAgYmdEQbKFEzonEKYgngJwgmfZggmjKQghgiBRGkBzeTgUikJRgc47LDErTnDEAkQJzkCJwYnEJzonEJIaddOwhJEJzgdBE4hYEJzieJADgnEE0KUCXzoAGkJLEiB2hOgQDBT0TsDT0YmlE4YmjkQ=")),
7: require("heatshrink").decompress(atob("qFGwkCkQAhkIpBiQlhkBSEJ8InlEIIoFE7whEE8pQFE7giBJQoneI4MCTYhQDE7YdCYYondEQYnEPwZ1bE5BQCJzonHkR2ZEAkBE4pNBE7zHFYrYhFUgonaXAQeEEwruZEYcgiROHJ7AfDAwxOeAAURiAmHE65HIOzwmOJ35OPE6xOPO35O/J35O/J1gnPEyx2PEy5OOOq5OnE5xOYO5omZgJQMJrQnLiQnagR4JOq5nCDgZ1fEYRLDE5DoZkUQNoZ4GOrJKGAoomXOw7lCAwYmYDgJSEAAUBA4QDBJzB6FOQrDXJwTJFdLjJKE9jDYZRAmkKAwmhKAgmiKAYmBkApdJIgjCKYIncOQYvJYTovGE84lagR2DE4xOakBOEgJXFOjYnEJAbtdOwggEkAmbDgInDE0B0BE4QgcE5AkiXYbpCOLonGYo4nhPMYnCUEgnBY0kiA==")),
8: require("heatshrink").decompress(atob("qFGwkCkQA/ABBSEJ8MgE4kBEsBPFE7xMCOIJ3hOYgFEE7rCGE70gE4pQBiAndYQwjBUohOZD4ZQFE7YkBE5AICYbZ2GE7sggJRCAA8iYzZOITroALE7EhExh4CAC0QExpPXOponZExx2XJ24nWdh52XdhzF/Yu5O/J35O0E55OXOx5O/J2omXE5x1XO54mYgQnMJrR4LOrciiAmiJgR4KEzIjDPBAlYiAiEeI51YkEBE4J5CD4KceTQQcBJgRQFdTZDCJIjDcNIqhGdTQmCkByFTTInDKgoAEE7ZEEJwhPdE1R1FE0InEE0R3DEwTGcDwomEE7hKFPYqafE8ROCE5DJbE5B/IEqh2ED4gnCJrMCJwgnEiB2bE4qeFEzUggQmIBQLEaEQImHLIImaE4YfcOw4lEFMLECS7onJO8wmkE4QljAAIA==")),
};
}
let rocketSequence = 1;
let settings = storage.readJSON("cassioWatch.settings.json", true) || {};
let rocketSpeed = settings.rocketSpeed || 700;
delete settings;
// 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 clearIntervals() {
if (rocketInterval) clearInterval(rocketInterval);
rocketInterval = undefined;
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
}
function drawClock() {
g.setFont("7x11Numeric7Seg", 3);
g.clearRect(80, 57, 170, 96);
g.setColor(0, 255, 255);
g.drawRect(80, 57, 170, 96);
g.fillRect(80, 57, 170, 96);
g.setColor(0, 0, 0);
g.drawString(require("locale").time(new Date(), 1), 70, 60);
g.setFont("8x12", 2);
g.drawString(require("locale").dow(new Date(), 2).toUpperCase(), 18, 130);
g.setFont("8x12");
g.drawString(require("locale").month(new Date(), 2).toUpperCase(), 80, 126);
g.setFont("8x12", 2);
const time = new Date().getDate();
g.drawString(time < 10 ? "0" + time : time, 78, 137);
}
function drawBattery() {
bigThenSmall(E.getBattery(), "%", 135, 21);
}
function drawRocket() {
let Rocket = getRocketSequences();
g.clearRect(5, 62, 63, 115);
g.setColor(0, 255, 255);
g.drawRect(5, 62, 63, 115);
g.fillRect(5, 62, 63, 115);
g.drawImage(Rocket[rocketSequence], 5, 65, { scale: 0.7 });
g.setColor(0, 0, 0);
rocketSequence = rocketSequence + 1;
if(rocketSequence > 8) rocketSequence = 1;
}
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.clear();
g.setColor(0, 255, 255);
g.fillRect(0, 0, g.getWidth(), g.getHeight());
let background = getBackgroundImage();
g.drawImage(background, 0, 0, { scale: 1 });
g.setColor(0, 0, 0);
g.setFont("6x12");
g.drawString("Launching Process", 30, 20);
g.setFont("8x12");
g.drawString("ACTIVATE", 40, 35);
g.setFontAlign(0,-1);
g.setFont("8x12", 2);
g.drawString(getTemperature(), 155, 132);
g.drawString(Math.round(Bangle.getHealthStatus("last").bpm), 109, 98);
g.drawString(getSteps(), 158, 98);
g.setFontAlign(-1,-1);
drawClock();
drawRocket();
drawBattery();
// Hide widgets
for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";}
}
Bangle.on("lcdPower", (on) => {
if (on) {
draw();
} else {
clearIntervals();
}
});
Bangle.on("lock", (locked) => {
clearIntervals();
draw();
if (!locked) {
rocketInterval = setInterval(drawRocket, rocketSpeed);
}
});
// Load widgets, but don't show them
Bangle.loadWidgets();
Bangle.setUI("clock");
g.reset();
g.clear();
draw();

BIN
apps/cassioWatch/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

1
apps/cassioWatch/icon.js Normal file
View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("lkswkGswAHtGIxGGBhATJAAYXNCYoWOFIwWNChQWKBYWYCxIqTxGJFgwnDnACBkUiCwuYFQo9ECYIAClAsJxIUElAUDwQWEyxAHBwITBmczmUiCwprHCgMjmUhiIYBA4JCGIAeCwQUBCYIXBiUyFggVCFQsziMjmdCkcxiZbBiJCEFQZUBmND93uolEochFgpWEIAUUCgIACp00iZVBzIVEAoJABmUeConu8cRiNEoMZNwIrCzEiiUxpwVF901IwNN6JuBtGJlAVBkchCg3umkSiMNCoIAEQIJWFkgCB8UYinUjIVFxEhKwszDAUU7tRCg2hilTH4xVB6kRzAUGBQIVECYVEorEDAAeBptBiUenw7BCYQACieCCosd6MYwUVBwNUAQYvBeYOJCYVoxVeoK5BAAq0C8MjiM4LALFCilNCAQpCN4lBmUoFQQVCxTlBiMieI3uqUoagIVDRAeCkclCgvlkciFYdmsxwDlESmTdE8lRmSCECosiFgMhqgUDkZABBwWYCoNpIIYXBmchiKLBkYqBNggVBNwIsECwMikQUBlEiBgWJCoRCEEQITDNIJrEIAQABZYOYnIWBFoQUGIAZCCTYYWCAAQUExIUDFggNDAA5AEFg4AIIAhvGEYU4ChgWHAAoUIWYpdFFRIWHChxEICZoWFFBIA=="))

View File

@ -0,0 +1,18 @@
{
"id": "cassioWatch",
"name": "Cassio Watch",
"description": "Animated Clock with Space Cassio Watch Style",
"screenshots": [{ "url": "screens/screen_night.png" },{ "url": "screens/screen_day.png" }],
"icon": "app.png",
"version": "0.10",
"type": "clock",
"tags": "clock, weather, cassio, retro",
"supports": ["BANGLEJS2"],
"allow_emulator": true,
"readme": "README.md",
"storage": [
{ "name": "cassioWatch.app.js", "url": "app.js" },
{"name":"cassioWatch.settings.js","url":"settings.js"},
{ "name": "cassioWatch.img", "url": "icon.js", "evaluate": true }
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View File

@ -0,0 +1,24 @@
(function(back) {
var FILE = "cassioWatch.settings.json";
var settings = Object.assign({
rocketSpeed: 700,
}, require('Storage').readJSON(FILE, true) || {});
function writeSettings() {
require('Storage').writeJSON(FILE, settings);
}
E.showMenu({
"" : { "title" : "Cassio Watch" },
"< Back" : () => back(),
'Rocket Speed': {
value: 0|settings.rocketSpeed,
min: 100, max: 60000,
onchange: v => {
settings.rocketSpeed = v;
writeSettings();
}
},
});
})

BIN
apps/cogclock/15x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

3
apps/cogclock/ChangeLog Normal file
View File

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

111
apps/cogclock/app.js Normal file
View File

@ -0,0 +1,111 @@
Graphics.prototype.setFont15x32N = function() {
this.setFontCustom(atob(
// 15x32.png, converted using http://ebfc.mattbrailsford.com/
"/////oAAAAKAAAACgAAAAoAAAAKAAAACgf//AoEAAQKB//8CgAAAAoAAAAKAAAACgAAAAoAAAAL////+/wAB/oEAAQKBAAECgf//AoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAAC////AgAAAQIAAAH+/w///oEIAAKBCAACgQgAAoEIAAKBCAACgQg/AoEIIQKB+CECgAAhAoAAIQKAACECgAAhAoAAIQL//+H+/w/h/oEIIQKBCCECgQghAoEIIQKBCCECgQghAoEIIQKB+D8CgAAAAoAAAAKAAAACgAAAAoAAAAL////+///gAIAAIACAACAAgAAgAIAAIAD/+CAAAAggAAAIIAAACD/+//gAAoAAAAKAAAACgAAAAoAAAAL////+///h/oAAIQKAACECgAAhAoAAIQKAACECgfghAoEIIQKBCD8CgQgAAoEIAAKBCAACgQgAAoEIAAL/D//+/////oAAAAKAAAACgAAAAoAAAAKAAAACgfg/AoEIIQKBCD8CgQgAAoEIAAKBCAACgQgAAoEIAAL/D//+/wAAAIEAAACBAAAAgQAAAIEAAACBAAAAgQAAAIH///6AAAACgAAAAoAAAAKAAAACgAAAAoAAAAL////+/////oAAAAKAAAACgAAAAoAAAAKAAAACgfg/AoEIIQKB+D8CgAAAAoAAAAKAAAACgAAAAoAAAAL////+///h/oAAIQKAACECgAAhAoAAIQKAACECgfghAoEIIQKB+D8CgAAAAoAAAAKAAAACgAAAAoAAAAL////+"
), "0".charCodeAt(0), 15, 32);
};
/**
* Add coordinates for nth tooth to vertices
* @param {array} poly Array to add points to
* @param {number} n Tooth number
*/
function addTooth(poly, n) {
const
tau = Math.PI*2, arc = tau/clock.teeth,
e = arc*clock.edge, p = arc*clock.point, s = (arc-(e+p))/2; // edge,point,slopes
const sin = Math.sin, cos = Math.cos,
x = clock.x, y = clock.y,
r2 = clock.r2, r3 = clock.r3;
let r = (n-1)*arc+e/2; // rads
poly.push(x+r2*sin(r), y-r2*cos(r));
r += s;
poly.push(x+r3*sin(r), y-r3*cos(r));
r += p;
poly.push(x+r3*sin(r), y-r3*cos(r));
r += s;
poly.push(x+r2*sin(r), y-r2*cos(r));
}
/**
* @param {number} n Tooth number to fill (1-based)
* @param col Fill color
*/
function fillTooth(n, col) {
if (!n) return; // easiest to check here
let poly = [];
addTooth(poly, n);
g.setColor(col).fillPoly(poly)
.setColor(g.theme.fg2).drawPoly(poly); // fillPoly colored over the outline
}
const ClockFace = require("ClockFace");
const clock = new ClockFace({
precision: 1,
settingsFile: "cogclock.settings.json",
init: function() {
this.r1 = 84; // inner radius
this.r3 = Math.min(Bangle.appRect.w/2, Bangle.appRect.h/2); // outer radius
this.r2 = (this.r1*3+this.r3*2)/5;
this.teeth = 12;
this.edge = 0.45;
this.point = 0.35; // as fraction of arc
this.x = Bangle.appRect.x+Bangle.appRect.w/2;
this.y = Bangle.appRect.y+Bangle.appRect.h/2;
},
draw: function(d) {
const x = this.x, y = this.y;
g.setColor(g.theme.bg2).fillCircle(x, y, this.r2) // fill cog
.setColor(g.theme.bg).fillCircle(x, y, this.r1) // clear center
.setColor(g.theme.fg2).drawCircle(x, y, this.r1); // draw inner border
let poly = []; // complete teeth outline
for(let t = 1; t<=this.teeth; t++) {
fillTooth(t, g.theme.bg2);
addTooth(poly, t);
}
g.drawPoly(poly, true); // draw outer border
if (!this.showDate) {
// draw top/bottom rectangles (instead of year/date)
g.reset()
.fillRect(x-30, y-60, x+29, y-33).clearRect(x-28, y-58, x+27, y-33)
.fillRect(x-30, y+60, x+29, y+30).clearRect(x-28, y+58, x+27, y+30);
}
this.tooth = 0;
this.update(d, {s: 1, m: 1, h: 1, d: 1});
},
update: function(d, c) {
g.reset();
const pad2 = num => (num<10 ? "0" : "")+num,
year = d.getFullYear(),
date = pad2(d.getDate())+pad2(d.getMonth()),
time = pad2(d.getHours())+pad2(d.getMinutes()),
tooth = Math.round(d.getSeconds()/60*this.teeth);
const x = this.x, y = this.y;
if (c.m) {
g.setFont("15x32N:2").setFontAlign(0, 0) // center middle
.drawString(time, x, y, true);
}
if (this.showDate) {
if (c.d) {
g.setFont("15x32N").setFontAlign(0, -1) // center top
.drawString(year, x, y+32, true)
.setFont("15x32N").setFontAlign(0, 1) // center bottom
.drawString(date, x, y-32, true);
}
}
if (tooth!==this.tooth) {
if (tooth>this.tooth) {
for(let t = this.tooth; t<=tooth; t++) { // fill missing teeth
fillTooth(t, g.theme.fg2);
}
} else {
for(let t = this.tooth; t>tooth; t--) { // erase extraneous teeth
fillTooth(t, g.theme.bg2);
}
}
}
this.tooth = tooth;
}
});
clock.start();

1
apps/cogclock/icon.js Normal file
View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwhC/ACcikQXpCQUCC4MgBAgqMCQIXEAgQXNBwIDCAggXOABAXLHwQAHMYQXmmczI5oiCBwUjCwIABmQXDEgJ0KCwMwCwMDDAgyGLYoWBgAXBgAYBMZIXEkYWBC4YYBGAh7FFwgHCC4YEBPRIwCFwYXFGAaqHC56oIIwgXFJAbUJLwpgHI4qPDIwpIFR4wWDLwa6BAAQHDVIYYCC/gYCC453MPIR3HU5gADd5bXHC4rvJMAYAECwJeCd5MjGAjVDC4ZHGNARIFGAgNDFw5IJUogwFC4gwBDAhGBBghIFBQhhBbYguEPAweCDAgACCwZACNg5LFXQYsIC5QAFdg4XcCxJHNBwYTEC6A+BJYQEEC5YYBMYhbCCxo0GCaIXbAHgA="))

BIN
apps/cogclock/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,20 @@
{
"id": "cogclock",
"name": "Cog Clock",
"version": "0.03",
"description": "A cross-shaped clock inside a cog",
"icon": "icon.png",
"screenshots": [{"url":"screenshot.png"}],
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS"],
"allow_emulator": true,
"storage": [
{"name":"cogclock.app.js","url":"app.js"},
{"name":"cogclock.settings.js","url":"settings.js"},
{"name":"cogclock.img","url":"icon.js","evaluate":true}
],
"data": [
{"name": "cogclock.settings.json"}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

10
apps/cogclock/settings.js Normal file
View File

@ -0,0 +1,10 @@
(function(back) {
let menu = {
"": {"title": /*LANG*/"Cog Clock"},
/*LANG*/"< Back": back,
};
require("ClockFace_menu").addSettingsFile(menu, "cogclock.settings.json", [
"showDate", "loadWidgets"
]);
E.showMenu(menu);
});

View File

@ -4,3 +4,4 @@
0.04: Fix for Bangle.js 2 and themes
0.05: Fix bearing not clearing correctly (visible in single or double digit bearings)
0.06: Add button for force compass calibration
0.07: Use 360-heading to output the correct heading value (fix #1866)

View File

@ -34,7 +34,7 @@ var oldHeading = 0;
Bangle.on('mag', function(m) {
if (!Bangle.isLCDOn()) return;
g.reset();
if (isNaN(m.heading)) {
if (isNaN(m.heading)) {
if (!wasUncalibrated) {
g.clearRect(0,24,W,48);
g.setFontAlign(0,-1).setFont("6x8");
@ -49,7 +49,7 @@ Bangle.on('mag', function(m) {
g.setFontAlign(0,0).setFont("6x8",3);
var y = 36;
g.clearRect(M-40,24,M+40,48);
g.drawString(Math.round(m.heading),M,y,true);
g.drawString(Math.round(360-m.heading),M,y,true);
}

View File

@ -1,7 +1,7 @@
{
"id": "compass",
"name": "Compass",
"version": "0.06",
"version": "0.07",
"description": "Simple compass that points North",
"icon": "compass.png",
"screenshots": [{"url":"screenshot_compass.png"}],

View File

@ -11,4 +11,5 @@
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.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
**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

View File

@ -89,7 +89,7 @@ function drawPage(p){
Bangle.on("swipe",(dirLeftRight, dirUpDown)=>{
selected = 0;
oldselected=-1;
if(settings.swipeExit && dirLeftRight==1) showClock();
if(settings.swipeExit && dirLeftRight==1) load();
if (dirUpDown==-1||dirLeftRight==-1){
++page; if (page>maxPage) page=0;
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){
if (n<0 || n>3) return false;
var x1 = (n%2)*72+XOFF; var y1 = n>1?72+YOFF:YOFF;

View File

@ -1,7 +1,7 @@
{
"id": "dtlaunch",
"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.",
"screenshots": [{"url":"shot1.png"},{"url":"shot2.png"},{"url":"shot3.png"}],
"icon": "icon.png",

View File

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

View File

@ -2,3 +2,5 @@
0.02: Shows the current week number (ISO8601), can be disabled via settings
0.03: Call setUI before loading widgets
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
function ISO8601_week_no(date) {
var tdt = new Date(date.valueOf());
@ -30,77 +11,49 @@ function ISO8601_week_no(date) {
return 1 + Math.ceil((firstThursday - tdt) / 604800000);
}
function d02(value) {
return ('0' + value).substr(-2);
function format(value) {
return ("0" + value).substr(-2);
}
function draw() {
g.reset();
g.clearRect(0, widget, viewport.width, viewport.height);
const now = new Date();
const ClockFace = require("ClockFace");
const clock = new ClockFace({
init: function () {
const appRect = Bangle.appRect;
const hour = d02(now.getHours() - (is12Hour && now.getHours() > 12 ? 12 : 0));
const minutes = d02(now.getMinutes());
const day = d02(now.getDate());
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);
this.viewport = {
width: appRect.w,
height: appRect.h
};
const centerTimeScaleX = center.x + 32 * scale;
g.setFontAlign(1, 0).setFont("Vector", 90 * scale);
g.drawString(hour, centerTimeScaleX, center.y - 31 * scale);
g.drawString(minutes, centerTimeScaleX, center.y + 46 * scale);
this.center = {
x: this.viewport.width / 2,
y: Math.round((this.viewport.height / 2) + appRect.y)
};
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", 16 * scale);
g.drawString(year, centerDatesScaleX, center.y - 62 * 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.setFontAlign(1, 0).setFont("Vector", 90 * this.scale);
g.drawString(format(hour), this.centerTimeScaleX, this.center.y - 31 * this.scale);
g.drawString(format(date.getMinutes()), this.centerTimeScaleX, this.center.y + 46 * this.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 *************************************/
let tickTimer;
function clearTickTimer() {
if (tickTimer) {
clearTimeout(tickTimer);
tickTimer = undefined;
}
}
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();
}
g.setFontAlign(-1, 0).setFont("Vector", 16 * this.scale);
g.drawString(date.getFullYear(date), this.centerDatesScaleX, this.center.y - 62 * this.scale);
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);
if (this.showWeekNum) g.drawString(format(ISO8601_week_no(date)), this.centerDatesScaleX, this.center.y + 15 * this.scale);
g.drawString(monthName, this.centerDatesScaleX, this.center.y + 48 * this.scale);
g.drawString(dayName, this.centerDatesScaleX, this.center.y + 66 * this.scale);
},
settingsFile: "ffcniftya.json"
});
Bangle.setUI("clock");
Bangle.loadWidgets();
Bangle.drawWidgets();
clock.start();

View File

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

View File

@ -3,3 +3,4 @@
0.03: Call setUI before loading widgets
Fix bug with black being unselectable
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"];
const color = Object.assign({ color: 63488 }, require("Storage").readJSON("ffcniftyb.json", true)).color; // Default to RED
var scale;
var screen;
var center;
var buf;
var img;
/* Clock *********************************************/
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) {
function format(value) {
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);
}
function renderText(g) {
const now = new Date();
function renderText(g, 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 minutes = d02(now.getMinutes());
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);
const monthName = require("date_utils").month(month, 1);
const dayName = require("date_utils").dow(date.getDay(), 1);
g.setFontAlign(1, 0).setFont("Vector", 90 * scale);
g.drawString(hour, center.x + 32 * scale, center.y - 31 * scale);
g.drawString(minutes, center.x + 32 * scale, center.y + 46 * scale);
g.drawString(format(hour), center.x + 32 * scale, center.y - 31 * scale);
g.drawString(format(date.getMinutes()), center.x + 32 * scale, center.y + 46 * scale);
g.setFontAlign(1, 0).setFont("Vector", 16 * scale);
g.drawString(year, center.x + 80 * scale, center.y - 42 * scale);
g.drawString(month, center.x + 80 * scale, center.y - 26 * scale);
g.drawString(day, center.x + 80 * scale, center.y - 10 * scale);
g.drawString(month2, center.x + 80 * scale, center.y + 44 * scale);
g.drawString(day2, center.x + 80 * scale, center.y + 60 * scale);
g.drawString(date.getFullYear(), center.x + 80 * scale, center.y - 42 * scale);
g.drawString(format(month), center.x + 80 * scale, center.y - 26 * scale);
g.drawString(format(date.getDate()), center.x + 80 * scale, center.y - 10 * scale);
g.drawString(monthName, center.x + 80 * scale, center.y + 44 * scale);
g.drawString(dayName, center.x + 80 * scale, center.y + 60 * scale);
}
const buf = Graphics.createArrayBuffer(screen.width, screen.height, 1, {
msb: true
const ClockFace = require("ClockFace");
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"
});
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();
clock.start();

View File

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

2
apps/football/ChangeLog Normal file
View File

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

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

@ -0,0 +1,3 @@
# Classic Football Chronometer Game
Context: https://www.anaitgames.com/analisis/analisis-casio-football-14

View File

@ -0,0 +1 @@
atob("MDABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAA/wAAAAAA/wAAAAAA/wAAAAAA/wAAAAAA/wAAAAAA/wAAAAAA/wAAAAAADwAAAAAADwAAAAAADwAAAAAADwAAAAAA//AAAAAA//AAAAAA//AAAAAA//AAAAAADw8AD/AADw8AD/AADw8AD/AADw8AD/AP/wAAD/AP/wAAD/AP/wAAD/AP/wAAD/AA8PAAAAAA8PAAAAAA8PAAAAAA8PAAAAAAAA8AAAAAAA8AAAAAAA8AAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")

474
apps/football/app.js Normal file
View File

@ -0,0 +1,474 @@
// globals. TODO: move into an object
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 = {
width: 75,
height: 128,
bpp: 1,
buffer: require('heatshrink').decompress(atob('AH4A/AH4A/AH4A/AH4A/AB0D/4AB+AJEBAX/BAk/CQ8PCQ4kDCQoIDCQgkDCQgkECQgIE4ASHFxH8JRgSEEgYSEPJAkEAH4A/AH4A/AH4A/AH4A/ACg='))
};
function loadDigits () {
digit[0] = {
width: 80,
height: 128,
bpp: 1,
buffer: require('heatshrink').decompress(atob('AH4AGn//AAngBIMfBIvABIMPBIuABIMHBIoIBg0DBAn+gYSBgIJE/kHBIOABIn4h4JB4F/BIfwj4JB8BQEAoIJBBoJOEv4JBEIJOEIwMHGoIJDIIIJBJIJOEBIQOCJwYJDOIR9DBISFCSIYJCTISlDBIXwBIZoBBP4J/BP4J/BNX+gED//gBIc/BIMB//ABIcf/gDB/+ABIcP/AhCBAYuBFoU+BIkDFoUcBIkBFoUIBIkAFogA/AAZPJMZJ3JRZKfIWZLHJbZL5bBP4J/BP4J/BKPgBIc/BIfABIcfBIeA/4AB/EPBIcBBIX8AwIJB/0DBIQECBIIOCAAQYBBIIiCAAQsBBIPwGwIAC4F/BIPgJQIACAoIJBBoIJDDIIJBJwZQDBIJODKAcAgxODKAZxBJwgABPYROEKASFDAAiRCJwhQCTYYAkA'))
};
digit[1] = {
width: 80,
height: 128,
bpp: 1,
buffer: require('heatshrink').decompress(atob('AH4A2wAIHgIJIgYJIg4JIh4JIj4JIn4JIv4JI/4JHgIJIgYJIg4JIh4JIj4JIn4J/BP4J/BP4Jqj//BA0Ah/+BI8H/gJHgf4BI8B+AJHgHgBJFABJAA/J55jIO5KLJT5KzJY5L5nBP4J/BP4J/AAcfBJEPBJEHBJEDBJEBBJEABJN/BJE/BJEfBJEPBJEHBJEDBJEBBJEABJIA/AAwA='))
};
digit[2] = {
width: 80,
height: 128,
bpp: 1,
buffer: require('heatshrink').decompress(atob('AH4AGj//AAnwBIMPBIvgBIMHBIvABIMDBIuABIMBBIsBGQQIE/0DBIV/BIf8g4JCn4JD/EPKA/wj4JCKAngn4JCKAnAv4JCKAmA/4JCKAgEBQYZOEBIgADFgIJHIAKlJBI5oBBP4J/BP4J/BOcfBJEP/wJHg/8Aof/AAP+gf4BAUBBIX/gPwBIUDBIeA8AiDBIfAoA2DBIYSDJQQACEwZeCAAQ6DgF/BJATJE5I7IghPFBIUOMYomDO4g6EwCLDJwgiDAAhyFTohKEToheEBP4J/BP4J/BOHwBJHgBJHAv////8BImABAP//wJEAIIACBIf+BImABIX8g4JD4AJC/EPBIZACgfwj4JDKgUD8E/BIZoCgZODKAkDJwZQEgcBBIhQCgROEKAhOEKAhOEKAhOEKAgAm'))
};
digit[3] = {
width: 80,
height: 128,
bpp: 1,
buffer: require('heatshrink').decompress(atob('AH4AGj//AAnwBIMPBIvgBIMHBIvABIMDBIuABIMBBIsBGQQIE/0DBIV/BIf8g4JCn4JD/EPKA/wj4JCKAngn4JCKAnAv4JCKAmA/4JCKAgEBQYZOEBIgADFgIJHIAKlJBI5oBBI58BBP4J/BP4J/BL8/BJEf/wJHh/8BI8H/AFD/4AB/0D+AICgIJDgPgBIUDBIQ5B4AiDBIeAwA2DBIYSDJQIJDEwZeCAAQ6DOQQACJwgTJE5I7JJ5JjEgIUDO4kDFAgJC/kDIwipNj4JIn7HIbZL5TBP4J/BP4J/BJs/BJEfBJEPBIgjB//8g4JDgIIB//+gYJDAgIACBwIJCDAIACwAJDFgIAC4F/IAgAC8E/KggAC+EfIgoAB/EPBIQIDKAROFKAZOGKAROGKAQJI4BOGKAQJI+CfHAEAA=='))
};
digit[4] = {
width: 80,
height: 128,
bpp: 1,
buffer: require('heatshrink').decompress(atob('AH4AswEBBJOABAoHBgPABIsDBIPgBIsHBIPwBIsPBIP4BIsfBIP8BIs/BIP+BIt/BIP/BIv/BIRQEAwQCBKAkDBIZQEg4JDKAkPBIZQEj4JIn4J/BP4J/BP4JjgAJFj//AYN/8AJDh/+C4QJEg/8C4XAv////+gYjCh+ABAIABgPwC4Q9BAAWAEYUCgYJD4FAFgYJDIAoJDEwRUDAARoGAAROCCZYnJHZJPJMZAABO46hCRYwAFT4YAFWYYAFY4YAFbYYJ/BP4J/BP4Jnj4JIh4JIg4JIgYJIgIJIgAJJv4JIn4JIj4JIh4JIg4JIgYJIgIJIgAJJAH4AGA='))
};
digit[5] = {
width: 80,
height: 128,
bpp: 1,
buffer: require('heatshrink').decompress(atob('AGUP/4AE/gJBg4JF/AJBgYJF+AJBgIJF8AJBwAJF4AJB4F/BImABIPgn4JEIoXwj4ID/wJC/BQEJwUA/hQEJwUA/xQEJwUA/5QEJwQJBKAhOCBIJQEJwQJBKAiVDFggAEIAgJFKgYJFNAYJ/BP4J/BP4Jmv/8BI8//AJHj/wBI8P8E//4ABBIcH4F/BIWABIUDwAIC//ABIUBgIJD8AeDgYJDGwkHBIZKEh4JDLwkfBIZyECZInJHZJPFkChEMYdwUIh3DFAiLDgKvIgbDIJQKvIUIgJFUIZ8FBP4J/BP4J/BL8PBJEHBJEDBJEBBIl//4ABwAJEBAQHBv4JCDAIAC8E/BIQsBAAXwj4JCIAIAC/EPBIRUBAAX8g4JCNAIAC/0DBI//gIJCJwZQCBIQIEKANAJwpQCJwxQCJwxQCJwxQCBJYAwA='))
};
digit[6] = {
width: 80,
height: 128,
bpp: 1,
buffer: require('heatshrink').decompress(atob('AGU//4AE8AJBj4JF4AJBh4JFwAJBg4JFBAMGgYIE/wSCgIJE/gJCwAJE/AJC4F/BIfwBIXgKAhOCg/wKAhOCg/4KAhOD/hQEOwUH/xQDJwRiCKAZOCBIRQDJwQJCGwQAEBIJKCBIxeCBP4J/BP4J/BOED//gBI0B//ABI0A/+ABI9/CoIAB/gJDnwpBAAP+BIccHoIACBIcIh4JDFgkfBIZAEBIhUEv4JDNAgJE/ATNn4nIHZBPGKARjFgIUCO4sDFASLFg48COQsPKARyGUILHGn6hBBIJyGco4J/BP4J/BP4Jm8AJDn4JD4AJDj4JDwAJDh4JDgP/AAP8AwIJB/0DBIQECBIIOCAAQYBBIP4EQIACwAJC+A2BAAXAv4JB8BKBAAQFBBIINBBIYZBBIIhBAAYtBBIJODKAcAgxODKAZnBJwhQCOIYAEPoROEKASZDAAilCJwhQCTYYAuA=='))
};
digit[7] = {
width: 80,
height: 128,
bpp: 4,
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] = {
width: 80,
height: 128,
bpp: 1,
buffer: require('heatshrink').decompress(atob('AGUf/4AE+AJBh4JF8AJBg4JF4AJBgYJFwAIBgIJFgOAgeABAn+A4MD4F/BIf8g4JB8E/BIf4h4JB+BQEAoIJBBoJOEn4JBEIJOEv4JBGoJOEAIIHBKAgEBBIRQDDAQJCKAYsCBISFCSIYJCTISlDBIX4BIZoBBP4J/BP4J/BNkB//wBIcf/4DB//gBIcP/wDBv/ABIcH/ghCwH/AAP+gYtCj4pBAAUBFoUOHoIACwAtCgkHBIfAoA2DBIZAEJQIACKgheBAARoGBKInJHZBPJMZJ3JRZKfJWZDHJfM4J/BP4J/BP4JP+AJDj4JD8AJDh4JD4AJDg4JDwH/AAP+AwQCBgIJCAgQJBBwQACDAIJB/giBAAXAv4JB/A2BAAXgn4JB+BKBAAQFBBIINBBIYZBBIIhBBIYtBBIJODKAYJBJwhQCwECJwhQCwBxCAAh9CJwhQCTIYAEUoROEKASbDAFwA='))
};
digit[9] = {
width: 80,
height: 128,
bpp: 1,
buffer: require('heatshrink').decompress(atob('AGUP/4AE/gJBg4JF/AJBgYJF+AJBgIJF8AJBwAJF4FAgHAv4JEwHAgHgn4JEgIJB+EfBAf+gYJB/BQE/kHBIIDBJwkPBIIXBJwkfBIIrBJwk/BIRQEJYIJCKAgOBBIXgIwYiBBIR7CQ4YJCR4SbDBISjCV4YJC/wJDFYIJ/BP4J/BP4Jjv/8BIcP/+AgE//AJDg//C4XwBIcDEYUP8E//4ABgIjCg/Av4JCwAjCgeABAQ5BuAJBgMBBIfgkAsDBIY2EIAIACJQhUBAAReENAIACOQgTJE5I7JJ5KhBMYwABO44ABRY4AFT4YAFWYYAFY4YAFbYYJ/BP4J/BP4Jnh4JIg4JIgYJIgIJEv//AAOABIgICA4N/BIQYBAAXgn4JCFgIAC+EfBIRABAAX4h4JCKgIAC/kHBIRoBAAX+gYJCn4JD/8BBIRODKAQJCBAhQBoBOFKAROGKAROGKAROGKAQJLAGA='))
};
}
// sprites
const left0 = {
width: 8,
height: 10,
bpp: 1,
transparent: 0,
buffer: atob('ADAwEDgUcCgEAA==')
};
const left1 = {
width: 8,
height: 10,
bpp: 1,
transparent: 0,
buffer: atob('ADAwEDh0ECAoAA==')
};
const left2 = {
width: 8,
height: 10,
bpp: 1,
transparent: 0,
buffer: atob('ADAwEBg4EBg0AA==')
};
const left4 = {
width: 8,
height: 10,
bpp: 1,
transparent: 0,
buffer: atob('ABgYVDgQEChEAA==')
};
const right0 = {
width: 8,
height: 10,
bpp: 1,
transparent: 0,
buffer: atob('AAwMCBwoDhQgAA==')
};
const right1 = {
width: 8,
height: 10,
bpp: 1,
transparent: 0,
buffer: atob('AAwMCBwuCAQUAA==')
};
const right2 = {
width: 8,
height: 10,
bpp: 1,
transparent: 0,
buffer: atob('AAwMCBgcCBgsAA==')
};
const right4 = left4;
const ball0 = {
width: 8,
height: 10,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAwMAAAAA==')
};
const ball1 = {
width: 8,
height: 10,
bpp: 1,
transparent: 0,
buffer: atob('AAAAAAAAGBgAAA==')
};
const gol01 = {
width: 8,
height: 10,
bpp: 1,
transparent: 0,
buffer: atob('ABhkhIS0sIAAAA==')
};
const gol11 = {
width: 8,
height: 10,
bpp: 1,
buffer: require('heatshrink').decompress(atob('gEYk0hkMthsBBAI='))
};
loadDigits();
function printNumber (n, x, y, options) {
if (n > 9 || n < -1) {
console.log(n);
return; // error
}
if (digit.length === 0) {
g.setColor(1, 1, 1);
if (options.scale == 0.2) {
g.setFont12x20(1);
} else {
g.setFont12x20(2.5);
}
g.drawString('' + n, x, y);
return;
}
const img = (n == -1) ? dash : digit[n];
if (img) {
// g.setColor(0,0,0);
// g.fillRect(x,y,x+32*options.scale,64*options.scale);
g.setColor(1, 1, 1);
g.drawImage(img, x, y, options);
}
}
g.setBgColor(0, 0, 0);
g.clear();
g.setColor(1, 1, 1);
function onStop () {
if (goalFrame > 0) {
return;
}
stopped = !stopped;
if (stopped) {
// Bangle.beep();
if (msinc == 0) {
// GOOL
if (owner == 0) {
score0++;
goaler = 0;
} else if (owner == 1) {
score1++;
goaler = 1;
}
goalFrame = 5;
}
let newOwner = 0;
if (msinc % 2) {
newOwner = 1;
} else {
newOwner = 0;
}
if (newOwner) {
seq0--;
seq1++;
} else {
seq0++;
seq1--;
}
if (seq0 < 0) seq0 = 0;
if (seq0 > 3) seq0 = 3;
if (seq1 < 0) seq1 = 0;
if (seq1 > 3) seq1 = 3;
owner = newOwner;
}
refresh();
refresh_ms();
}
function onButtonPress() {
console.log('on.tap');
setWatch(() => {
onButtonPress();
}, BTN1);
Bangle.beep();
if (endGame) {
score0 = 0;
score1 = 0;
seq0 = 0;
seq1 = 0;
part = 0;
inc = 0;
msinc = 0;
stopped = true;
endGame = false;
} else {
if (inc == 0) {
// autogame();
stopped = false;
} else {
onStop();
}
}
}
setWatch(() => {
onButtonPress();
}, BTN1);
/*Bangle.on('tap', function () {
onButtonPress();
});
*/
g.setFont12x20(3);
function refresh () {
g.clear();
if (inc > 59) {
inc = 0;
part++;
}
if (part >= 2 && inc > 30) {
part = 2;
Bangle.buzz();
endGame = true;
endInc = inc;
inc = 0;
}
if (inc > 44) {
inc = 0;
if (part < 2) {
part++;
}
if (part >= 2) {
if (score0 != score1) {
Bangle.buzz();
endGame = true;
endInc = inc;
inc = 0;
}
}
// end of 1st or 2nd part of the game?
}
let two = (inc < 10) ? '0' + inc : '' + inc;
if (endGame) {
g.setColor(0, 0, 0);
g.fillRect(0, 64, w, h);
if (inc % 2) {
two = (endInc < 10) ? '0' + endInc : '' + endInc;
printNumber(-1, 2, 64 + 16, { scale: 0.4 });
printNumber(part, 34, 64 + 16, { scale: 0.4 });
printNumber(two[0], 74, 64 + 16, { scale: 0.4 });
printNumber(two[1], 74 + 32, 64 + 16, { scale: 0.4 });
}
} else {
// seconds
printNumber(0, 2, 64 + 16, { scale: 0.4 });
printNumber(part, 34, 64 + 16, { scale: 0.4 });
printNumber(two[0], 74, 64 + 16, { scale: 0.4 });
printNumber(two[1], 74 + 32, 64 + 16, { scale: 0.4 });
}
refresh_ms();
refresh_score();
refresh_pixels();
}
function refresh_pixels () {
let frame4 = inc % 2;
if (goalFrame > 0) {
frame4 = goalFrame % 2;
if (goaler == 0) {
g.drawImage(frame4 ? right4 : right0, 20, 10, { scale: 5 });
g.setColor(1, 1, 1);
g.drawImage(gol11, w - 50, 10, { scale: 5 });
} else if (goaler == 1) {
g.drawImage(frame4 ? left0 : left4, w - 50, 10, { scale: 5 });
g.setColor(1, 1, 1);
g.drawImage(gol01, 30, 10, { scale: 5 });
}
return;
}
if (endGame) {
if (score0 > score1) {
g.drawImage(frame4 ? right1 : right0, 5 + (seq0 * 10), 10, { scale: 5 });
} else if (score0 < score1) {
g.drawImage(frame4 ? left0 : left1, w - 30 - (seq1 * 10), 10, { scale: 5 });
}
return;
}
g.drawImage(frame4 ? right1 : right0, 5 + (seq0 * 10), 10, { scale: 5 });
g.drawImage(frame4 ? left0 : left1, w - 30 - (seq1 * 10), 10, { scale: 5 });
let bx = (owner == 0) ? w / 3 : w / 2;
bx += 2;
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;
function refresh_dots () {
if (endGame) {
dots = 0;
} else {
dots++;
}
if (dots % 2) {
g.setColor(1, 1, 1);
} else {
g.setColor(0, 0, 0);
}
const x = 67;
let y = 100;
g.fillRect(x, y, x + 4, y + 4);
y += 16;
g.fillRect(x, y, x + 4, y + 4);
}
const h = g.getHeight();
function refresh_ms () {
if (endGame) {
if (inc % 2) {
printNumber(-1, 80 + 64 - 4, 64 + 16 + 8 + 16, { scale: 0.2 });
printNumber(-1, 80 + 64 + 16 - 4, 64 + 32 + 8, { scale: 0.2 });
}
return;
}
// nanoseconds
if (msinc > 59) {
msinc = 0;
}
g.setColor(0, 0, 0);
g.fillRect(80 + 64, h / 2, 80 + 64 + 32, g.getHeight());
const two = (msinc < 10) ? '0' + msinc : '' + msinc;
printNumber(two[0], 80 + 64 - 4, 64 + 16 + 8 + 16, { scale: 0.2 });
printNumber(two[1], 80 + 64 + 16 - 4, 64 + 32 + 8, { scale: 0.2 });
}
function refresh_score () {
g.setColor(0, 0, 0);
g.fillRect(0, h - 32, w, h);
let two = (score0 < 10) ? '0' + score0 : '' + score0;
printNumber(two[0], 64 - 16, 32 + 64 + 16 + 8 + 16, { scale: 0.2 });
printNumber(two[1], 64, 32 + 64 + 32 + 8, { scale: 0.2 });
two = (score1 < 10) ? '0' + score1 : '' + score1;
printNumber(two[0], 32 + 64, 32 + 64 + 16 + 8 + 16, { scale: 0.2 });
printNumber(two[1], 32 + 64 + 16, 32 + 64 + 32 + 8, { scale: 0.2 });
}
refresh();
setInterval(function () {
if (!stopped || endGame) {
inc++;
}
if (goalFrame > 0) {
goalFrame--;
}
refresh();
}, 1000);
setInterval(function () {
refresh_dots();
}, 500);
setInterval(function () {
if (!stopped) {
msinc++;
if (msinc > 59) {
msinc = 0;
}
}
}, 10);
setInterval(function () {
if (!stopped) {
refresh_ms();
}
}, 250);
function autogame () {
if (endGame) {
return;
}
onStop();
if (stopped) {
setTimeout(autogame, 500);
} else {
setTimeout(autogame, 315 + 10 * (Math.random() * 5));
}
}
Bangle.setOptions({ lockTimeout: 0, backlightTimeout: 0 });
// autogame();

BIN
apps/football/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 B

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