Merge branch 'master' into development
|
@ -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']
|
|
@ -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,6 +1,5 @@
|
|||
.htaccess
|
||||
node_modules
|
||||
package-lock.json
|
||||
.DS_Store
|
||||
*.js.bak
|
||||
appdates.csv
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
Bangle.js App Loader (and Apps)
|
||||
================================
|
||||
|
||||
[](https://app.travis-ci.com/github/espruino/BangleApps)
|
||||
[](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
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
|
@ -0,0 +1,11 @@
|
|||
# two of them clock
|
||||
|
||||
You can now wear teh memez on your wrist.
|
||||
|
||||

|
||||
|
||||
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)
|
|
@ -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"))
|
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 5.8 KiB |
|
@ -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}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 7.1 KiB |
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
})();
|
|
@ -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
|
||||
*/
|
||||
}
|
||||
})();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: Basic agenda with events from GB
|
|
@ -0,0 +1,3 @@
|
|||
# Agenda
|
||||
|
||||
Basic agenda reading the events synchronised from GadgetBridge
|
|
@ -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"))
|
|
@ -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();
|
After Width: | Height: | Size: 3.0 KiB |
|
@ -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}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.7 KiB |
|
@ -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);
|
||||
})
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -4,3 +4,7 @@ A simple digital clock showing seconds as a horizontal bar.
|
|||
| 24hr style | 12hr style |
|
||||
| --- | --- |
|
||||
|  |  |
|
||||
|
||||
## Settings
|
||||
* `Show date`: display date at the bottom of screen
|
||||
* `Font`: choose between bitmap or vector fonts
|
|
@ -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();
|
||||
|
|
|
@ -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"}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
|
@ -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...)
|
|
@ -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.
|
|
@ -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();
|
||||
}
|
||||
});
|
After Width: | Height: | Size: 2.7 KiB |
|
@ -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//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////"))
|
After Width: | Height: | Size: 2.3 KiB |
|
@ -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"}]
|
||||
}
|
|
@ -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()
|
|
@ -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.
|
||||
|
||||

|
||||
|
||||
## Creator
|
||||
|
||||
Created by [Deirdre O'Byrne](https://github.com/deirdreobyrne)
|
|
@ -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");
|
|
@ -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"))
|
After Width: | Height: | Size: 7.9 KiB |
|
@ -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}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 3.1 KiB |
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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"}],
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
1.00: New App!
|
||||
1.01: Use fractional numbers and scale the points to keep working consistently on whole screen
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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());
|
||||
});
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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.
|
|
@ -0,0 +1,11 @@
|
|||
# cassioWatch
|
||||
|
||||
 
|
||||
|
||||
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.
|
|
@ -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();
|
After Width: | Height: | Size: 4.7 KiB |
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("lkswkGswAHtGIxGGBhATJAAYXNCYoWOFIwWNChQWKBYWYCxIqTxGJFgwnDnACBkUiCwuYFQo9ECYIAClAsJxIUElAUDwQWEyxAHBwITBmczmUiCwprHCgMjmUhiIYBA4JCGIAeCwQUBCYIXBiUyFggVCFQsziMjmdCkcxiZbBiJCEFQZUBmND93uolEochFgpWEIAUUCgIACp00iZVBzIVEAoJABmUeConu8cRiNEoMZNwIrCzEiiUxpwVF901IwNN6JuBtGJlAVBkchCg3umkSiMNCoIAEQIJWFkgCB8UYinUjIVFxEhKwszDAUU7tRCg2hilTH4xVB6kRzAUGBQIVECYVEorEDAAeBptBiUenw7BCYQACieCCosd6MYwUVBwNUAQYvBeYOJCYVoxVeoK5BAAq0C8MjiM4LALFCilNCAQpCN4lBmUoFQQVCxTlBiMieI3uqUoagIVDRAeCkclCgvlkciFYdmsxwDlESmTdE8lRmSCECosiFgMhqgUDkZABBwWYCoNpIIYXBmchiKLBkYqBNggVBNwIsECwMikQUBlEiBgWJCoRCEEQITDNIJrEIAQABZYOYnIWBFoQUGIAZCCTYYWCAAQUExIUDFggNDAA5AEFg4AIIAhvGEYU4ChgWHAAoUIWYpdFFRIWHChxEICZoWFFBIA=="))
|
|
@ -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 }
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 63 KiB |
After Width: | Height: | Size: 60 KiB |
|
@ -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();
|
||||
}
|
||||
},
|
||||
});
|
||||
})
|
After Width: | Height: | Size: 6.1 KiB |
|
@ -0,0 +1,3 @@
|
|||
0.01: New clock
|
||||
0.02: Use ClockFace library, add settings
|
||||
0.03: Use ClockFace_menu.addSettingsFile
|
|
@ -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();
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwhC/ACcikQXpCQUCC4MgBAgqMCQIXEAgQXNBwIDCAggXOABAXLHwQAHMYQXmmczI5oiCBwUjCwIABmQXDEgJ0KCwMwCwMDDAgyGLYoWBgAXBgAYBMZIXEkYWBC4YYBGAh7FFwgHCC4YEBPRIwCFwYXFGAaqHC56oIIwgXFJAbUJLwpgHI4qPDIwpIFR4wWDLwa6BAAQHDVIYYCC/gYCC453MPIR3HU5gADd5bXHC4rvJMAYAECwJeCd5MjGAjVDC4ZHGNARIFGAgNDFw5IJUogwFC4gwBDAhGBBghIFBQhhBbYguEPAweCDAgACCwZACNg5LFXQYsIC5QAFdg4XcCxJHNBwYTEC6A+BJYQEEC5YYBMYhbCCxo0GCaIXbAHgA="))
|
After Width: | Height: | Size: 11 KiB |
|
@ -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"}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 5.7 KiB |
|
@ -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);
|
||||
});
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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"}],
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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": [
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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();
|
|
@ -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"}],
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
|
@ -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"}],
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
1.00: Initial implementation
|
||||
1.01: Bug fixes and performance and visual improvements
|
|
@ -0,0 +1,3 @@
|
|||
# Classic Football Chronometer Game
|
||||
|
||||
Context: https://www.anaitgames.com/analisis/analisis-casio-football-14
|
|
@ -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")
|
|
@ -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();
|
||||
|
After Width: | Height: | Size: 232 B |
After Width: | Height: | Size: 96 B |
After Width: | Height: | Size: 95 B |