2021-03-20 18:05:48 +00:00
/ * *
* 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 ) {
phoneClearMessage ( ) ;
phone . messageTimeout = setTimeout ( ( ) => phone . message = null , ONE _SECOND * 30 ) ;
phone . message = msg ;
phone . messageType = type ;
// Notify user and active screen
if ( ! ( require ( 'Storage' ) . readJSON ( 'setting.json' , 1 ) || { } ) . quiet ) {
Bangle . buzz ( ) ;
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 ( ) ;