mirror of https://github.com/espruino/BangleApps
718 lines
18 KiB
JavaScript
718 lines
18 KiB
JavaScript
/**
|
|
* BangleJS MARIO CLOCK
|
|
*
|
|
* + Original Author: Paul Cockrell https://github.com/paulcockrell
|
|
* + Created: April 2020
|
|
* + Based on Espruino Mario Clock V3 https://github.com/paulcockrell/espruino-mario-clock
|
|
* + Online Image convertor: https://www.espruino.com/Image+Converter, Use transparency + compression + 8bit Web + export as Image String
|
|
* + Images must be drawn as PNGs with transparent backgrounds
|
|
*/
|
|
|
|
const locale = require("locale");
|
|
const storage = require('Storage');
|
|
const settings = (storage.readJSON('setting.json', 1) || {});
|
|
const timeout = settings.timeout || 10;
|
|
const is12Hour = settings["12hour"] || false;
|
|
|
|
// Screen dimensions
|
|
let W, H;
|
|
// Screen brightness
|
|
let brightness = 1;
|
|
|
|
let intervalRef, displayTimeoutRef = null;
|
|
|
|
// Colours
|
|
const LIGHTEST = "#effedd";
|
|
const LIGHT = "#add795";
|
|
const DARK = "#588d77";
|
|
const DARKEST = "#122d3e";
|
|
const NIGHT = "#001818";
|
|
|
|
// Character names
|
|
const DAISY = "daisy";
|
|
const TOAD = "toad";
|
|
const MARIO = "mario";
|
|
|
|
const characterSprite = {
|
|
frameIdx: 0,
|
|
x: 33,
|
|
y: 55,
|
|
jumpCounter: 0,
|
|
jumpIncrement: Math.PI / 6,
|
|
isJumping: false,
|
|
character: MARIO,
|
|
};
|
|
|
|
const coinSprite = {
|
|
frameIdx: 0,
|
|
x: 34,
|
|
y: 18,
|
|
isAnimating: false,
|
|
yDefault: 18,
|
|
};
|
|
|
|
const pyramidSprite = {
|
|
x: 90,
|
|
height: 34,
|
|
};
|
|
|
|
const ONE_SECOND = 1000;
|
|
const DATE_MODE = "date";
|
|
const BATT_MODE = "batt";
|
|
const TEMP_MODE = "temp";
|
|
const PHON_MODE = "gbri";
|
|
|
|
let timer = 0;
|
|
let backgroundArr = [];
|
|
let nightMode = false;
|
|
let infoMode = DATE_MODE;
|
|
|
|
// Used to stop values flapping when displayed on screen
|
|
let lastBatt = 0;
|
|
let lastTemp = 0;
|
|
|
|
const phone = {
|
|
get status() {
|
|
return NRF.getSecurityStatus().connected ? "Yes" : "No";
|
|
},
|
|
message: null,
|
|
messageTimeout: null,
|
|
messageScrollX: null,
|
|
messageType: null,
|
|
};
|
|
|
|
const SETTINGS_FILE = "marioclock.json";
|
|
|
|
function readSettings() {
|
|
return require('Storage').readJSON(SETTINGS_FILE, 1) || {};
|
|
}
|
|
|
|
function writeSettings(newSettings) {
|
|
require("Storage").writeJSON(SETTINGS_FILE, newSettings);
|
|
}
|
|
|
|
function phoneOutbound(msg) {
|
|
Bluetooth.println(JSON.stringify(msg));
|
|
}
|
|
|
|
function phoneClearMessage() {
|
|
if (phone.message === null) return;
|
|
|
|
if (phone.messageTimeout) {
|
|
clearTimeout(phone.messageTimeout);
|
|
phone.messageTimeout = null;
|
|
}
|
|
phone.message = null;
|
|
phone.messageScrollX = null;
|
|
phone.messageType = null;
|
|
}
|
|
|
|
function phoneNewMessage(type, msg) {
|
|
Bangle.buzz();
|
|
|
|
phoneClearMessage();
|
|
phone.messageTimeout = setTimeout(() => phone.message = null, ONE_SECOND * 30);
|
|
phone.message = msg;
|
|
phone.messageType = type;
|
|
|
|
// Notify user and active screen
|
|
if (!Bangle.isLCDOn()) {
|
|
clearTimers();
|
|
Bangle.setLCDPower(true);
|
|
}
|
|
}
|
|
|
|
function truncStr(str, max) {
|
|
if (str.length > max) {
|
|
return str.substr(0, max) + '...';
|
|
}
|
|
return str;
|
|
}
|
|
|
|
function phoneInbound(evt) {
|
|
switch (evt.t) {
|
|
case 'notify':
|
|
const sender = truncStr(evt.sender, 10);
|
|
const subject = truncStr(evt.subject, 15);
|
|
phoneNewMessage("notify", `${sender} - '${subject}'`);
|
|
break;
|
|
case 'call':
|
|
if (evt.cmd === "accept") {
|
|
let nameOrNumber = "Unknown";
|
|
if (evt.name !== null || evt.name !== "") {
|
|
nameOrNumber = evt.name;
|
|
} else if (evt.number !== null || evt.number !== "") {
|
|
nameOrNumber = evt.number;
|
|
}
|
|
phoneNewMessage("call", nameOrNumber);
|
|
}
|
|
break;
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function genRanNum(min, max) {
|
|
return Math.floor(Math.random() * (max - min + 1) + min);
|
|
}
|
|
|
|
function switchCharacter() {
|
|
const curChar = characterSprite.character;
|
|
|
|
let newChar;
|
|
switch(curChar) {
|
|
case DAISY:
|
|
newChar = MARIO;
|
|
break;
|
|
case TOAD:
|
|
newChar = DAISY;
|
|
break;
|
|
case MARIO:
|
|
default:
|
|
newChar = TOAD;
|
|
}
|
|
|
|
characterSprite.character = newChar;
|
|
}
|
|
|
|
function toggleNightMode() {
|
|
if (!nightMode) {
|
|
nightMode = true;
|
|
return;
|
|
}
|
|
|
|
brightness -= 0.30;
|
|
if (brightness <= 0) {
|
|
brightness = 1;
|
|
nightMode = false;
|
|
}
|
|
Bangle.setLCDBrightness(brightness);
|
|
}
|
|
|
|
function incrementTimer() {
|
|
if (timer > 100) {
|
|
timer = 0;
|
|
}
|
|
else {
|
|
timer += 10;
|
|
}
|
|
}
|
|
|
|
function drawBackground() {
|
|
"ram"
|
|
|
|
// Clear screen
|
|
const bgColor = (nightMode) ? NIGHT : LIGHTEST;
|
|
g.setColor(bgColor);
|
|
g.fillRect(0, 10, W, H);
|
|
|
|
// set cloud colors and draw clouds
|
|
const cloudColor = (nightMode) ? DARK : LIGHT;
|
|
g.setColor(cloudColor);
|
|
g.fillRect(0, 10, W, 15);
|
|
g.fillRect(0, 17, W, 17);
|
|
g.fillRect(0, 19, W, 19);
|
|
g.fillRect(0, 21, W, 21);
|
|
|
|
// Date bar
|
|
g.setColor(DARKEST);
|
|
g.fillRect(0, 0, W, 9);
|
|
}
|
|
|
|
function drawFloor() {
|
|
const fImg = require("heatshrink").decompress(atob("ikDxH+rgATCoIBQAQYDP")); // Floor image
|
|
for (let x = 0; x < 4; x++) {
|
|
g.drawImage(fImg, x * 20, g.getHeight() - 5);
|
|
}
|
|
}
|
|
|
|
function drawPyramid() {
|
|
"ram"
|
|
|
|
const pPol = [pyramidSprite.x + 10, H - 5, pyramidSprite.x + 50, pyramidSprite.height, pyramidSprite.x + 90, H - 5]; // Pyramid poly
|
|
|
|
const color = (nightMode) ? DARK : LIGHT;
|
|
g.setColor(color);
|
|
g.fillPoly(pPol);
|
|
|
|
pyramidSprite.x -= 1;
|
|
// Reset and randomize pyramid if off-screen
|
|
if (pyramidSprite.x < - 100) {
|
|
pyramidSprite.x = 90;
|
|
pyramidSprite.height = genRanNum(25, 60);
|
|
}
|
|
}
|
|
|
|
function drawTreesFrame(x, y) {
|
|
const tImg = require("heatshrink").decompress(atob("h8GxH+AAMHAAIFCAxADEBYgDCAQYAFCwobOAZAEFBxo=")); // Tree image
|
|
|
|
g.drawImage(tImg, x, y);
|
|
g.setColor(DARKEST);
|
|
g.drawLine(x + 6 /* Match stalk to palm tree */, y + 6 /* Match stalk to palm tree */, x + 6, H - 6);
|
|
}
|
|
|
|
function generateTreeSprite() {
|
|
return {
|
|
x: 90,
|
|
y: genRanNum(30, 60)
|
|
};
|
|
}
|
|
|
|
function drawTrees() {
|
|
// remove first sprite if offscreen
|
|
let firstBackgroundSprite = backgroundArr[0];
|
|
if (firstBackgroundSprite) {
|
|
if (firstBackgroundSprite.x < -15) backgroundArr.splice(0, 1);
|
|
}
|
|
|
|
// set background sprite if array empty
|
|
let lastBackgroundSprite = backgroundArr[backgroundArr.length - 1];
|
|
if (!lastBackgroundSprite) {
|
|
const newSprite = generateTreeSprite();
|
|
lastBackgroundSprite = newSprite;
|
|
backgroundArr.push(lastBackgroundSprite);
|
|
}
|
|
|
|
// add random sprites
|
|
if (backgroundArr.length < 2 && lastBackgroundSprite.x < (16 * 7)) {
|
|
const randIdx = Math.floor(Math.random() * 25);
|
|
if (randIdx < 2) {
|
|
const newSprite = generateTreeSprite();
|
|
backgroundArr.push(newSprite);
|
|
}
|
|
}
|
|
|
|
for (x = 0; x < backgroundArr.length; x++) {
|
|
let scenerySprite = backgroundArr[x];
|
|
scenerySprite.x -= 5;
|
|
drawTreesFrame(scenerySprite.x, scenerySprite.y);
|
|
}
|
|
}
|
|
|
|
function drawCoinFrame(x, y) {
|
|
const cImg = require("heatshrink").decompress(atob("hkPxH+AAcHAAQIEBIXWAAQNEBIWHAAdcBgQLBA4IODBYQKEBAQMDBelcBaJUBM4QRBNYx1EBQILDR4QHBBISdIBIoA==")); // Coin image
|
|
g.drawImage(cImg, x, y);
|
|
}
|
|
|
|
function drawCoin() {
|
|
if (!coinSprite.isAnimating) return;
|
|
|
|
coinSprite.y -= 8;
|
|
if (coinSprite.y < (0 - 15 /*Coin sprite height*/)) {
|
|
coinSprite.isAnimating = false;
|
|
coinSprite.y = coinSprite.yDefault;
|
|
return;
|
|
}
|
|
|
|
drawCoinFrame(coinSprite.x, coinSprite.y);
|
|
}
|
|
|
|
function drawDaisyFrame(idx, x, y) {
|
|
var frame;
|
|
|
|
switch(idx) {
|
|
case 2:
|
|
frame = require("heatshrink").decompress(atob("h0UxH+AAkrAIgAH60rAIQNIBQIABDZErAAwMMBwo0CBxQNEHAQGCBpIPCBoQJCDRIXDBpA7DBIQACw5yCJQgZDP4gNErlcJAZ6GAgNcw+HRI4CCDgNcU44ZDDYSYGDIYACB4QaEDYgMFJAg3DFQ5mFBQYA==")); // daisy jumping
|
|
break;
|
|
case 0:
|
|
frame = require("heatshrink").decompress(atob("h8UxH+AAsHAIgAI60HAIQOJBYIABDpMHAAwNNB4wOJB4gIEHgQBBBxYQCBwYLDDhIaEBxApEw4qDAgIOHDwiIEBwtcFIRWIUgWHw6TIAQXWrlcWZAqBDQIeBBxQaBDxIcCHIQ8JDAIAFWJLPHA=="));
|
|
break;
|
|
case 1:
|
|
default:
|
|
frame = require("heatshrink").decompress(atob("h8UxH+AAsHAIgAI60HAIQOJBYIABDpMHAAwNNB4wOJB4gIEHgQBBBxYQCBwYLDDhIaEBxApEw4qDAgIOHDwiIEBwtcFIRWIUgQvBSZACCBwNcWZQcCAAIPIDgYACFw4YBDYIOCD4waEDYI+HaBQ="));
|
|
}
|
|
|
|
g.drawImage(frame, x, y);
|
|
}
|
|
|
|
function drawMarioFrame(idx, x, y) {
|
|
var frame;
|
|
|
|
switch(idx) {
|
|
case 2:
|
|
frame = require("heatshrink").decompress(atob("h8UxH+AAkrAAYFCBo9cAAIEB63WB4gMDB4YOFBowfDw4xDBAYADA4YcDGwYACDoYAEBYYBBw4NDCoYOFDIweFFwoZFAQYIDLAQWGEwqgECI6ECJ4JeGQYS9EB4QTHBwImCBYRtDSAwrFawqkFWY7PEBxoMFKoZaELoYICAAg")); // Mario frame jumping
|
|
break;
|
|
case 0:
|
|
frame = require("heatshrink").decompress(atob("h8UxH+AAkrAAYKFBolcAAIPIBgYPDBpgfGFIY7EA4YcEBIPWAAYdDC4gLDAII5ECoYOFDogODFgoJCBwYZCAQYOFBAhAFFwZKGGQgNCw4ACLwgFBBwgKECQpZCCgRqDFQikEJIriIBgzwIdxjiGBxIuEBIo=")); // Mario Frame 1
|
|
break;
|
|
case 1:
|
|
default:
|
|
frame = require("heatshrink").decompress(atob("h8UxH+AAkrAAYKFBolcAAIPIBgYPDBpgfGFIY7EA4YcEBIPWAAYdDC4gLDAII5ECoYOFDogODFgoJCBwYZCAQYOFBAhAFFwZKGHQpMDw+HCQYEBSowOBBQIeJDAQODSwaVHUhwOLfg4FHe4wASA=")); // Mario frame 2
|
|
}
|
|
|
|
g.drawImage(frame, x, y);
|
|
}
|
|
|
|
function drawToadFrame(idx, x, y) {
|
|
var frame;
|
|
|
|
switch(idx) {
|
|
case 2:
|
|
frame = require("heatshrink").decompress(atob("iEUxH+ACkrAAoNJrnWAAQRGlfWrgACB4QEBCAYOBB44QFB4QICAg4QBBAQbDEgwPCHpAGCGAQ9KAYQPENwoTEH4crw4EDAAgGDB4YABAYIBDP4YLEAAIPHCAQHCCAQTDD4gHDEA4PFGAY3EbooPECob8IPooPFCATGEf44hFAAYLDA==")); // toad jumping
|
|
break;
|
|
case 0:
|
|
frame = require("heatshrink").decompress(atob("iEUxH+ACkHAAoNJrnWAAQRGg/WrgACB4QEBCAYOBB44QFB4QICAg4QBBAQbDEgwPCHpAGCGAQ9KAYQPKCYg/EJAoADAwaKFw4BEP4YQCBIIABB468EB4QADYIoQGDwQOGBYYrCCAwbFFwgQEM4gAEeA4OIH4ghFAAYLD")); // Toad Frame 1
|
|
break;
|
|
case 1:
|
|
default:
|
|
frame = require("heatshrink").decompress(atob("iEUxH+ACkHAAoNJrnWAAQRGg/WrgACB4QEBCAYOBB44QFB4QICAg4QBBAQbDEgwPCHpAGCGAQ9KAYQPKCYg/EJAoADAwaKFw4BEP4YQCBIIABB468EB4QADYIoQGDwQOGBYQrDb4wcGFxYLDMoYgHRYgwKABAMBA")); // Mario frame 2
|
|
}
|
|
|
|
g.drawImage(frame, x, y);
|
|
}
|
|
|
|
// Mario speach bubble
|
|
function drawNotice(x, y) {
|
|
if (phone.message === null) return;
|
|
|
|
let img;
|
|
switch (phone.messageType) {
|
|
case "call":
|
|
img = require("heatshrink").decompress(atob("h8PxH+AAMHABIND6wAJB4INEw9cAAIPFBxAPEBw/WBxYACDrQ7QLI53OSpApDBoQAHB4INLByANNAwo="));
|
|
break;
|
|
case "notify":
|
|
img = require("heatshrink").decompress(atob("h8PxH+AAMHABIND6wAJB4INCrgAHB4QOEDQgOIAIQFGBwovDA4gOGFooOVLJR3OSpApDBoQAHB4INLByANNAwoA="));
|
|
break;
|
|
case "lowBatt":
|
|
img = require("heatshrink").decompress(atob("h8PxH+AAMHABIND6wAJB4INFrgABB4oOEBoQPFBwwDGB0uHAAIOLJRB3OSpApDBoQAHB4INLByANNAwo"));
|
|
break;
|
|
}
|
|
|
|
if (img) g.drawImage(img, characterSprite.x, characterSprite.y - 16);
|
|
}
|
|
|
|
function drawCharacter(date, character) {
|
|
"ram"
|
|
|
|
// calculate jumping
|
|
const seconds = date.getSeconds(),
|
|
milliseconds = date.getMilliseconds();
|
|
|
|
if (seconds == 59 && milliseconds > 800 && !characterSprite.isJumping) {
|
|
characterSprite.isJumping = true;
|
|
}
|
|
|
|
if (characterSprite.isJumping) {
|
|
characterSprite.y = (Math.sin(characterSprite.jumpCounter) * -12) + 50 /* Character Y base value */;
|
|
characterSprite.jumpCounter += characterSprite.jumpIncrement;
|
|
|
|
if (parseInt(characterSprite.jumpCounter) === 2 && !coinSprite.isAnimating) {
|
|
coinSprite.isAnimating = true;
|
|
}
|
|
|
|
if (characterSprite.jumpCounter.toFixed(1) >= 4) {
|
|
characterSprite.jumpCounter = 0;
|
|
characterSprite.isJumping = false;
|
|
}
|
|
}
|
|
|
|
// calculate animation timing
|
|
if (timer % 20 === 0) {
|
|
// shift to next frame
|
|
if (characterSprite.isJumping) {
|
|
characterSprite.frameIdx = 2;
|
|
} else {
|
|
characterSprite.frameIdx = characterSprite.frameIdx == 0 ? 1 : 0;
|
|
}
|
|
}
|
|
|
|
switch(characterSprite.character) {
|
|
case DAISY:
|
|
drawDaisyFrame(characterSprite.frameIdx, characterSprite.x, characterSprite.y);
|
|
break;
|
|
case TOAD:
|
|
drawToadFrame(characterSprite.frameIdx, characterSprite.x, characterSprite.y);
|
|
break;
|
|
case MARIO:
|
|
default:
|
|
drawMarioFrame(characterSprite.frameIdx, characterSprite.x, characterSprite.y);
|
|
}
|
|
}
|
|
|
|
function drawBrickFrame(x, y) {
|
|
const brk = require("heatshrink").decompress(atob("ikQxH+/0HACASB6wAQCoPWw4AOrgT/Cf4T/Cb1cAB8H/wVBAB/+A"));
|
|
g.drawImage(brk, x, y);
|
|
}
|
|
|
|
function drawTime(date) {
|
|
// draw hour brick
|
|
drawBrickFrame(20, 25);
|
|
// draw minute brick
|
|
drawBrickFrame(42, 25);
|
|
|
|
const h = date.getHours();
|
|
const hours = ("0" + ((is12Hour && h > 12) ? h - 12 : h)).substr(-2);
|
|
const mins = ("0" + date.getMinutes()).substr(-2);
|
|
|
|
g.setFont("6x8");
|
|
g.setColor(DARKEST);
|
|
g.drawString(hours, 25, 29);
|
|
g.drawString(mins, 47, 29);
|
|
}
|
|
|
|
function buildDateStr(date) {
|
|
let dateStr = locale.date(date, true);
|
|
dateStr = dateStr.replace(date.getFullYear(), "").trim().replace(/\/$/i,"");
|
|
dateStr = locale.dow(date, true) + " " + dateStr;
|
|
|
|
return dateStr;
|
|
}
|
|
|
|
function buildBatStr() {
|
|
let batt = parseInt(E.getBattery());
|
|
const battDiff = Math.abs(lastBatt - batt);
|
|
|
|
// Suppress flapping values
|
|
// Only update batt if it moves greater than +-2
|
|
if (battDiff > 2) {
|
|
lastBatt = batt;
|
|
} else {
|
|
batt = lastBatt;
|
|
}
|
|
|
|
const battStr = `Bat: ${batt}%`;
|
|
|
|
return battStr;
|
|
}
|
|
|
|
function buildTempStr() {
|
|
let temp = parseInt(E.getTemperature());
|
|
const tempDiff = Math.abs(lastTemp - temp);
|
|
|
|
// Suppress flapping values
|
|
// Only update temp if it moves greater than +-2
|
|
if (tempDiff > 2) {
|
|
lastTemp = temp;
|
|
} else {
|
|
temp = lastTemp;
|
|
}
|
|
const tempStr = `Temp: ${temp}'c`;
|
|
|
|
return tempStr;
|
|
}
|
|
|
|
function buildPhonStr() {
|
|
return `Phone: ${phone.status}`;
|
|
}
|
|
|
|
function drawInfo(date) {
|
|
let xPos;
|
|
let str = "";
|
|
|
|
if (phone.message !== null) {
|
|
str = phone.message;
|
|
const strLen = g.stringWidth(str);
|
|
if (strLen > W) {
|
|
if (phone.messageScrollX === null || (phone.messageScrollX <= (strLen * -1))) {
|
|
phone.messageScrollX = W;
|
|
resetDisplayTimeout();
|
|
} else {
|
|
phone.messageScrollX -= 2;
|
|
}
|
|
xPos = phone.messageScrollX;
|
|
} else {
|
|
xPos = (W - g.stringWidth(str)) / 2;
|
|
}
|
|
} else {
|
|
switch(infoMode) {
|
|
case PHON_MODE:
|
|
str = buildPhonStr();
|
|
break;
|
|
case TEMP_MODE:
|
|
str = buildTempStr();
|
|
break;
|
|
case BATT_MODE:
|
|
str = buildBatStr();
|
|
break;
|
|
case DATE_MODE:
|
|
default:
|
|
str = buildDateStr(date);
|
|
}
|
|
xPos = (W - g.stringWidth(str)) / 2;
|
|
}
|
|
|
|
g.setFont("6x8");
|
|
g.setColor(LIGHTEST);
|
|
g.drawString(str, xPos, 1);
|
|
}
|
|
|
|
function changeInfoMode() {
|
|
phoneClearMessage();
|
|
|
|
switch(infoMode) {
|
|
case BATT_MODE:
|
|
infoMode = TEMP_MODE;
|
|
break;
|
|
case TEMP_MODE:
|
|
infoMode = PHON_MODE;
|
|
break;
|
|
case PHON_MODE:
|
|
infoMode = DATE_MODE;
|
|
break;
|
|
case DATE_MODE:
|
|
default:
|
|
infoMode = BATT_MODE;
|
|
}
|
|
}
|
|
|
|
function redraw() {
|
|
const date = new Date();
|
|
|
|
// Update timers
|
|
incrementTimer();
|
|
|
|
// Draw frame
|
|
drawBackground();
|
|
drawFloor();
|
|
drawPyramid();
|
|
drawTrees();
|
|
drawTime(date);
|
|
drawInfo(date);
|
|
drawCharacter(date);
|
|
drawNotice();
|
|
drawCoin();
|
|
|
|
// Render new frame
|
|
g.flip();
|
|
}
|
|
|
|
function clearTimers(){
|
|
if(intervalRef) {
|
|
clearInterval(intervalRef);
|
|
intervalRef = null;
|
|
}
|
|
|
|
if(displayTimeoutRef) {
|
|
clearInterval(displayTimeoutRef);
|
|
displayTimeoutRef = null;
|
|
}
|
|
}
|
|
|
|
function resetDisplayTimeout() {
|
|
if (displayTimeoutRef) clearInterval(displayTimeoutRef);
|
|
|
|
displayTimeoutRef = setInterval(() => {
|
|
if (Bangle.isLCDOn()) Bangle.setLCDPower(false);
|
|
clearTimers();
|
|
}, ONE_SECOND * timeout);
|
|
}
|
|
|
|
function startTimers(){
|
|
if(intervalRef) clearTimers();
|
|
intervalRef = setInterval(redraw, 50);
|
|
|
|
resetDisplayTimeout();
|
|
|
|
redraw();
|
|
}
|
|
|
|
function loadSettings() {
|
|
const settings = readSettings();
|
|
if (!settings) return;
|
|
|
|
if (settings.character) characterSprite.character = settings.character;
|
|
if (settings.nightMode) nightMode = settings.nightMode;
|
|
if (settings.brightness) {
|
|
brightness = settings.brightness;
|
|
Bangle.setLCDBrightness(brightness);
|
|
}
|
|
}
|
|
|
|
function updateSettings() {
|
|
const newSettings = {
|
|
character: characterSprite.character,
|
|
nightMode: nightMode,
|
|
brightness: brightness,
|
|
};
|
|
writeSettings(newSettings);
|
|
}
|
|
|
|
function checkBatteryLevel() {
|
|
if (Bangle.isCharging()) return;
|
|
if (E.getBattery() > 10) return;
|
|
if (phone.message !== null) return;
|
|
|
|
phoneNewMessage("lowBatt", "Warning, battery is low");
|
|
}
|
|
|
|
// Main
|
|
function init() {
|
|
loadSettings();
|
|
|
|
clearInterval();
|
|
|
|
// Initialise display
|
|
Bangle.setLCDMode("80x80");
|
|
|
|
// Store screen dimensions
|
|
W = g.getWidth();
|
|
H = g.getHeight();
|
|
|
|
// Get Mario to jump!
|
|
setWatch(() => {
|
|
if (intervalRef && !characterSprite.isJumping) characterSprite.isJumping = true;
|
|
resetDisplayTimeout();
|
|
phoneClearMessage(); // Clear any phone messages and message timers
|
|
}, BTN3, {repeat: true});
|
|
|
|
// Close watch and load launcher app
|
|
setWatch(() => {
|
|
Bangle.setLCDMode();
|
|
Bangle.showLauncher();
|
|
}, BTN2, {repeat: false, edge: "falling"});
|
|
|
|
// Change info mode
|
|
setWatch(() => {
|
|
changeInfoMode();
|
|
}, BTN1, {repeat: true});
|
|
|
|
Bangle.on('lcdPower', (on) => on ? startTimers() : clearTimers());
|
|
|
|
Bangle.on('faceUp', (up) => {
|
|
if (up && !Bangle.isLCDOn()) {
|
|
clearTimers();
|
|
Bangle.setLCDPower(true);
|
|
}
|
|
});
|
|
|
|
Bangle.on('swipe', (sDir) => {
|
|
resetDisplayTimeout();
|
|
|
|
switch(sDir) {
|
|
// Swipe right (1) - change character (on a loop)
|
|
case 1:
|
|
switchCharacter();
|
|
break;
|
|
// Swipe left (-1) - change day/night mode (on a loop)
|
|
case -1:
|
|
default:
|
|
toggleNightMode();
|
|
}
|
|
|
|
updateSettings();
|
|
});
|
|
|
|
// Phone connectivity
|
|
try { NRF.wake(); } catch (e) {}
|
|
|
|
NRF.on('disconnect', () => {
|
|
phoneNewMessage(null, "Phone disconnected");
|
|
});
|
|
|
|
NRF.on('connect', () => {
|
|
setTimeout(() => {
|
|
phoneOutbound({ t: "status", bat: E.getBattery() });
|
|
}, ONE_SECOND * 2);
|
|
phoneNewMessage(null, "Phone connected");
|
|
});
|
|
|
|
GB = (evt) => phoneInbound(evt);
|
|
|
|
startTimers();
|
|
|
|
setInterval(checkBatteryLevel, ONE_SECOND * 60 * 10);
|
|
checkBatteryLevel();
|
|
}
|
|
|
|
// Initialise!
|
|
init(); |