Merge branch 'yngv27-master'

pull/793/head^2
Gordon Williams 2021-08-25 08:44:27 +01:00
commit 397fce2842
13 changed files with 1063 additions and 0 deletions

View File

@ -3410,5 +3410,33 @@
{"name":"thermomF.app.js","url":"app.js"},
{"name":"thermomF.img","url":"app-icon.js","evaluate":true}
]
},
{ "id": "nixie",
"name": "Nixie Clock",
"shortName":"Nixie",
"icon": "nixie.png",
"version":"0.01",
"description": "A nixie tube clock for both Bangle 1 and 2.",
"tags": "clock",
"type":"clock",
"readme": "README.md",
"storage": [
{"name":"nixie.app.js","url":"app.js"},
{"name":"nixie.img","url":"app-icon.js","evaluate":true},
{"name":"m_vatch.js","url":"m_vatch.js"}
]
},
{ "id": "carcrazy",
"name": "Car Crazy",
"shortName":"Car Crazy",
"icon": "carcrash.png",
"version":"0.01",
"description": "A simple car game where you try to avoid the other cars by tilting your wrist left and right. Hold down button 2 to start.",
"tags": "game",
"readme": "README.md",
"storage": [
{"name":"carcrazy.app.js","url":"app.js"},
{"name":"carcrazy.img","url":"app-icon.js","evaluate":true}
]
}
]

1
apps/carcrazy/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: Car Crazy is now avialable for testing in beta!

18
apps/carcrazy/README.md Normal file
View File

@ -0,0 +1,18 @@
# Car Crazy
Car crazy is a fun game where you tilt your wrist left and right to avoid incoming cars. If you get hit by a car you lose a heart. In the game you have three hearts, if you get hit 3 times you are sent to the game over screen. Your goal is to try to last as long as you can. Because this game is still in beta please report any bugs here: https://forms.office.com/r/HnwYzG9Sk7.
### Images:
(Coming Soon)
### Instructions:
BNT2: Hold down this button to start the game if you are on the starting page and game over page.
Tilting Left-Right: Tilt your wrist left and right to steer your car and try not to get hit by the enemy car.
### Feautures Coming Soon:
0.02: Levels are creating making the game get harder as it goes along.
0.03: Optional soundtrack in settings. More levels.
0.04: With higher scores you can now unlock different colors of cars.

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwhC/AH4Ay90A8AWT8HuhwXSCQPuAAIXShwWCC6YuDAAQwWGKQWWFwoJDxAAFF5gHChAXODAhHCxGDmYAE3YAC2AwKxAWFC4gABC4YBCIwYXGoNb2tUpYxDRYsIIw0zqoXBqtVDAIXVDAJiEUYYXNqhhDUYgWGC41VSYgXSjYXHAA4XGJAULDQQXQJALFDGBTXFAAuwgQXV3cP+coC6m////nAXYiIAEC5m4FIYXN2gKBpYXFwIXMkIKBiQXSFwQwCC4hIFC42xiMjmURlYXFDAgXImcziMbC4ynKC4uxC5MAAAsBI4cQAoMRC50AKYYdCDYIQHGBASCC6QwCDgwA/AH4AVA=="))

240
apps/carcrazy/app.js Normal file
View File

@ -0,0 +1,240 @@
Bangle.setLCDPower(1);
Bangle.setLCDTimeout(0);
var numberofHearts = 3;
Bangle.setLCDMode("doublebuffered");
//var popUp = require("heatshrink").decompress(atob("isFwMBCJoA=="));
var backgroundImage = require("heatshrink").decompress(atob("isZxH+woAB6YBBBodXAgYLCCIQUJAgoUOFP4p/FP4p/FP4p/FP4pYA=="));
var heartImage = require("heatshrink").decompress(atob("hUKxH+ABckAAYIDpFstlIudzpARCtmQyGXz2luYVBkgIBBQXCy4JCCYQKCtgpCE4IJCFQIyFBAwKDBAw/CAogA=="));
var gameOverImage = require("heatshrink").decompress(atob("kcV4MB+Nj/4AJwATBgfwDAV+AYUP4ADBgP4BAU/AYUHERMP+AiBgP+EQU/4AiBg/4EQX/EQUf8EH8ED/kAAIN/wEfEQ9/FwX+A=="));
var RedCar =
require("heatshrink").decompress(atob("ol7xH+ABWBqwAqwI5LABKAtIf4AClZASq0rp9zudIDYNIABgSBABd5AAtPEoNPAoUrqxDPlcAkmXy9OIYVsABYSBABWdzwAFIYVzAoMkRSJDCNAJDBkhDiuckFQOevMqklPZBo7DZAUkHagAKIoyKCkl5IwIwBwJDLYYRDrWoMAAgJDCqxDZyxDXzoABIcpAXRRRD/If5D/If5D/If5D9uYASId9OBoIAPpxDvBoRD2AAlOADlPAAckIbJ0QABjeNIf4ACkhDUIgNIUQQEBAAtyIaOXvIAFp4lBp4FBIapFCA4wACyxCRzo2BAAlzEoNzA4ZDVQ4ZDGQyRDHQ4RDgHyQAHIf5DhklIHoRDrudzA4RDNIoRDtFYRD/Iac5rsykgABaANyILQACzudIgTGClcrrowBIZ9ktdkRQhCdRQ8rGAxD/If5D/If4ACzpD/Io5D/IbOI2eIIdowBwJDP1nX1hDtGAJD/Ialkx7eCZdgwDIZ1rIdwwEIf5D/If5DZmU5mRDtGAhDNAAhDrAAhD/IaM5rrLmzpCCIYYwEIZtktdkIcpBCIYgwEIf5DZAARD/If8ylcrpFIIcd5uYpBmRDUAAUyA4JECIb9zEoNdFoZDVnJDkp5DZx+z2YHCIbudAAJDFFYOPIanX65DgIAIACIYWBFYJD/IYSHBx5DQ1idCx5DFAAjITIIjLFFYIvBwJDQLINrtc5IZOWIbq1DIaJXBslkmUHg9IAA1yRCV5AAtPg8rnIrBQ6VkbwcyDQMrAoIAEpxCRpwaGmSwBmUAg4wEIZ1rIYYdBg5DZpBDLGAt6II+BwJ8CIY85mQAFp9OACFPDQ1dIZMrHYJCFLghDHAA4lCAB85DpgwEAARD9rpDHq0rp9zAAcrmTQCIZVkroAQGoNr2YABIY4wCHAlPldWBgMky4ADkhTDIZQAV2fXIZA4JlYABQ4pDCg8HIcePAAJDCFYJDCYAoABZoOBdw0HoFAVoQAdIASKFslkIgSMEIAIACIZBBgIonXZwpDHgBDE/1Wq0rIe8rHYJDFIoRD3IJBD6vRD1AASH/AA5DWoFkAARD9IgIADoA+hrooEGQhDQAAk5IcM5FpJDNkgAGnNAaQjTTC4gACmUklQAEkhDPpFsAAoYCR68yDQ1PzwAFvJD/IbNyuVOAA1WnIAQp4aGuedzpDauWXABFIOgwAKpwdJIghDgpxD/AAVzACRDvADpD/If5D/If5D/If5D/If5D/If5D/If5DGthD/AApD/If8kkhFGuVyIMJCDp4ABIZ5FCRIyKgQgmekgzDIf5DRp1zpzOIZ4I+dZAUqldzvLLQueXuaKiIQ2elYrBAgN5IaSHBRAIADIcOdp8klSSBOgRDNHIQiBpzjDRjDIEvMkEYdzBAIPBIbUAIboiEIaWBq1WYYVyy9yZwTQGAAVOp9OABlzAAdPAAlzzuduYeBGoJCJAAcrYYZFBAAReCADUkRgeezomBaYMrIJpD/Z496YoRFEUoIAaYoQgBEIMkp+BwJCQAARkCRQKJDAD2WEoKEBgBBTZw1zIcNyIYbIRAA1WRQQABEQIAbEAQlBY6hDIAARCcpAiEIbWBlYADpAAcEQhCZAAyrCADI+hAAhCbIc+BqwAawIwS"));
var OrangeCar = require("heatshrink").decompress(atob("ol7xH+ABXGAFfHHJYAJQFpD/Ia1OgEkpwDBAAUqABYSBABd4AAskEgQFBAgOcQiQXBlQdCGhdyGowAFznGAAnHNQVO4/GRSQSCGIJDPIRhDH4xDD0SHCkhAMCAQ7BIAQbBHaYAKIoyEDKII1C45DJLIRDw0XHIYXGIa5BYZwWiIZKHDIeZFCIf5D/If5D/If5D/If4ADBQgAMIeEkBoQAOlRDvISJDnAAkklQAWDAQbGIcB0JABreNIf5DYIgJFEW4xCRJZYeBznGIagABIQYHDAAQ7SGwQAD44mCp3HBARD64xDDA4ZDbYzRDdPghDs45DQgEkHoRDrE4MkIaCKDIdYyEIf5DRzjeDaAZBaAAOcAAI9B0RmBlQqB0QwDIZvP5/ORQhCcRRAqCGAhD1Q4RD/If5D/ABOiIf6KHIf5DZ63X55DtGAhDN6/X6xDtGAPQIf5DT5/WZdwwB5xDQ54SCIdgwDIf5D/If5DZvHGAgRDrGAhDNAApDqAAhD/IaOc494Icui0RCBIYeiGAZDN5/P5xDkQgRDFGAhD/IbIABuRD/AAZD658qkkAlUqIcdyuUkkkqIaoABvAHBIgRDfFoXGFoZD+45DW6HX6+cIb+czmiIYnOFYJDU6wXB4xDfIAIACIYvQIf5DVCgPX63WZYoAEZCZBEIYnHFYLNCIaSKBvBDoFYKJCIafOIYUklQAFZKecAAsqEoOi5xDCQ6BYBCQVO5zjCAApESkgaGpwqBIwQEBFYRDPCQRD54/HDARDHzlOAAdyuTTHABd4CwIACp3HIZMAHYJCEPQpDHAAjYCTQQAPzgWCAAxDIAAJDXIgRDi5pDHpy0BXokAdoZDK56lBAB4UC64ABIY4wCHAkkkl4BgV4AAZTEIZYAUIZY4LI4IACuQUFITxDD6HQA4TpFIY7NB47vHQj4AE6yKG5w1HIAIACBpBDk6DOHIZn+lTdFIekqpxDFAALeFIeXGII5D/IeHP63X6xD/ABBD/IbIAE45Dh44tJIfHGIb9445FCaCzrGvBDXlVOAAodIISI7HknGAAtyIeQzCIZhTCIad4vEqkgAFp2cACAZGDQZDaIQIAIeZIAIlQdJ0RDkkhD/AAbbEABgcKIcwAcIf5D/If5D/If5D/If5D/If5D/If5D/If5DKI1BDVIQxD8AAMkIc+cAAJBCp0qGYRDPRVBBDAAIyEIf5DRlSbDZwtOuQ+eAAIrCNQIECIZofBRgaJfIQyED43HvBDPPYJDKRC6GD0WcAAUkFQILBIaB9EToRIJRqDIEzgiFBAR0DIZsqPgZDp46FBIZ3OGAMkkkqCogACBQIAFKwIATEQl4aIIJBD4RCJAAZ/EN4olEADCMDZAQlDIJpD/Z47pCZwQACYSgAIzg/CvEqeAPH5xCQRRoAdEoKETIdlyIbQABcgZIDADYiE45BXAAPGIcMqEQnPIbPOEAkqADgiEITIAGEogAYH0BD/ABHH4wAa5wwSA"));
var PurpleCar = require("heatshrink").decompress(atob("ol74UBitg///BIP/7lVqtUDJUVBwIABq2qABOVCCkolQJC0AwDgWolAQD1EqwBCH0EqCCdKxQuEAAkKwGhCH4Q/CGD7ECDINEAAoQIwBACgQQYwQ+EAAcC1EACAeAlQfDAAheBCG2oCBUCCB8qCCiQBdp3+fxW/CH4Q/CH4QxgQQKwAQD1QQK1QQUmQQKxiH/CBGqCB3/5WACA+j/4Qf14Q/KhWqRI0CCAv+x4QBEYcC1Wo/SpFCBtUlQQBBgISBAAUAgYQB1fVr2qCAP//8qB4WoAwIQB1WVCCEoFwIQCAA36GwMo1AQOlQQM+QQCqWq1YPIAAPqKgYQOqwQOiqpBCBi6DCH4QDYoIOH/gKBCAs/CD2AZoISEBwUAgAQYAYIABgWqfIIQGAAISBAAQHCCDIAICH4Q/CH4Q/CAWoB5UKCB8qCCmK0AxJwQQDgQQKwAQE1Q0ICAuAlQQHlQrBCCdy1EK1QABCAmq0EKgoQBqofBIoIAFWYMqB4QQRuQ5BCAxlBxoQDqyHKyoQUZpLJDCBmq1oQFvRTGAAOlCApFBB4xBFCCdUIY3VBgY="));
var LightGreenCar = require("heatshrink").decompress(atob("ol74UBocF///BIP1z9VqtUDJUVBwIABq2qABOVCCkolQJC0AwDgWolAQD1EqwBCH0EqCCdKxQuEAAkKwGhCH4Q/CGD7ECDINEAAoQIwBACgQQYwQ+EAAcC1EACAeAlQfDAAheBCG2oCBUCCB8qCCiQBdp3+fxW/CH4Q/CH4QxgQQKwAQD1QQK1QQUmQQKxiH/CBGqCB3/5WACA+j/4Qf14QxKjGqRI0CCAv+x4QBEYcC1Wo/SpFCBtUlQQBBgISBAAQGBCAOo6te1QQB///lQPC1AGBCAOqyoQQlAuBCAXPAQIAD/Q2BlGoCAgAGCAUqCBnyCAVS1WrB5AAB9RUDCB1WCB0VVIIQMXQYQ/CAbFBBw/8BQIQFn4QewDNBCQgOCgEACDADBAAMC1T5BCAwABCQIACA4QQZABAQ/CH4Q/CH4QC1APKhQQPlQQUxWgGJOCCAcCCBWACAmqGhAQFwEqCA8qFYIQTuWohWqAAIQE1WghUFCANVD4JFBAAqzBlQPCCCNyHIIQGMoONCAdWQ5WVCCjNJZIYQM1WtCAt6KYwAB0oQFIoIPGIIoQTqhDG6oMDA"));
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min) + min); //The maximum is exclusive and the minimum is inclusive
}
function moveEnemyPosition(){
score += 1;
randomRoadPositionIndicator = getRandomInt(1, 4);
if ((randomRoadPositionIndicator == 1)) {
randomRoadPosition = 85;
}else if((randomRoadPositionIndicator == 2)){
randomRoadPosition = 120;
}else {
randomRoadPosition = 155;
}
}
function collision(){
if(gameStatus == GAMEPLAYING){
if
(
(enemyCarFrontY > playerCarFrontY)
&&
(
(enemyCarLeftX > playerCarLeftX && enemyCarLeftX < playerCarRightX)
||
(enemyCarRightX > playerCarLeftX && enemyCarRightX < playerCarRightX)
)
){
// hit
setTimeout(collision, 2500); // wait 2.5 second for the function to actiavte agian.
numberofHearts -= 1;
score -= 1;
Bangle.buzz();
}else{
// miss
setTimeout(collision, 1); // try again in 1 milliseconds.
}
}
}
function storeMyData(data) {
// ensure there are less than 500 elements in the array
while (log.length >= 500) log.shift();
// append a new item to the array
log.push(data);
}
var file = require("Storage").open("CarCrazy.csv","r");
var currentHighScore = file.readLine();
if (currentHighScore == undefined) currentHighScore = 0;
var BackgroundStartingPosition = 75;
var carScale = 0.5;
var accel = Bangle.getAccel();
var playerCarPosition = 120-accel.x*40;
var BackgroundYPosition = BackgroundStartingPosition;
var randomRoadPositionIndicator = getRandomInt(1, 3);
var randomRoadPosition = 120;
var enemyPositonY = 30;
var carWidth = 30;
var carHeight = 60;
var playerCarY = 130;
var enemyCarLeftX;
var enemyCarRightX;
var playerCarLeftX;
var playerCarRightX;
var enemyCarFrontY;
var playerCarFrontY;
var GAMEPLAYING = 1;
var GAMEOVER = 2;
var GAMESTART = 3;
var gameStatus = GAMESTART;
var score = 0;
moveEnemyPosition();
collision();
g.setFontAlign(-1,-1);
function clearHighScore() {
currentHighScore = 0;
file = require("Storage").open("CarCrazy.csv","w");
file.erase();
}
function draw(){
if(gameStatus == GAMEPLAYING){
BackgroundYPosition += 10;
accel = Bangle.getAccel();
playerCarPosition = 120-accel.x*40;
g.flip();
g.drawImage(backgroundImage,125,BackgroundYPosition, {scale:13,rotate:0});
g.drawImage(RedCar,playerCarPosition,playerCarY, {scale:carScale,rotate:3.142});
g.drawImage(OrangeCar,randomRoadPosition,enemyPositonY, {scale:carScale,rotate:0});
if(numberofHearts==3){
g.drawImage(heartImage,10,10, {scale:2,rotate:0});
g.drawImage(heartImage,10,50, {scale:2,rotate:0});
g.drawImage(heartImage,10,30, {scale:2,rotate:0});
}else if(numberofHearts==2){
g.drawImage(heartImage,10,50, {scale:2,rotate:0});
g.drawImage(heartImage,10,30, {scale:2,rotate:0});
}else if(numberofHearts==1){
g.drawImage(heartImage,10,50, {scale:2,rotate:0});
}else{
gameStatus = GAMEOVER;
//clearHighScore();
if(score >= currentHighScore){
currentHighScore = score;
file = require("Storage").open("CarCrazy.csv","w");
file.erase();
file = require("Storage").open("CarCrazy.csv","w");
file.write(currentHighScore+"\n");
}
}
playerCarFrontY = playerCarY-carHeight/2;
playerCarBackY = playerCarY+carHeight/2;
playerCarLeftX = playerCarPosition-carWidth/2;
playerCarRightX = playerCarPosition+carWidth/2;
enemyCarFrontY = enemyPositonY+carHeight/2;
enemyCarBackY = enemyPositonY-carHeight/2;
enemyCarLeftX = randomRoadPosition-carWidth/2;
enemyCarRightX = randomRoadPosition+carWidth/2;
//g.drawRect(playerCarLeftX, playerCarFrontY, playerCarRightX, playerCarBackY);
//g.drawRect(enemyCarLeftX, enemyCarFrontY, enemyCarRightX, enemyCarBackY);
g.setColor(0,0,0);
g.drawString("Score: "+score,180,5);
g.drawString("HighScore:",178,15);
g.drawString(currentHighScore,205,25);
if(BackgroundYPosition > 170){
BackgroundYPosition = BackgroundStartingPosition;
}
}else if(gameStatus == GAMEOVER){
BackgroundYPosition += 10;
g.flip();
g.drawImage(backgroundImage,125,BackgroundYPosition, {scale:13,rotate:0});
g.drawImage(gameOverImage,125,80, {scale:8,rotate:0});
if(BackgroundYPosition > 170){
BackgroundYPosition = BackgroundStartingPosition;
}
g.setColor(255,0,0);
g.setFont("6x8",4);
g.drawString("Game Over",13,17);
g.setFont("6x8",1.5);
g.drawString("Score: "+score,10,75);
g.drawString("High",10,100);
g.drawString("Score: " + currentHighScore,10,110);
g.drawString("Hold Button",10,130);
g.drawString("2 To Play",10,140);
g.drawImage(LightGreenCar,180,115, {scale:0.5,rotate:3});
g.drawImage(PurpleCar,215,115, {scale:0.5,rotate:3});
}else if(gameStatus == GAMESTART){
g.flip();
g.drawImage(backgroundImage,125,BackgroundYPosition, {scale:13,rotate:0});
g.setColor(255,0,0);
BackgroundYPosition += 10;
g.setFont("6x8",3);
g.drawImage(gameOverImage,125,80, {scale:8,rotate:0});
g.drawString("Welcome to",13,11);
g.drawString("Car Crazy",13,31);
g.setFont("6x8",1.8);
g.drawString("High",10,80);
g.drawString("Score: "+currentHighScore,10,90);
g.drawString("Hold Button",10,120);
g.drawString("2 To Start",10,130);
g.drawImage(LightGreenCar,180,115, {scale:0.5,rotate:3});
g.drawImage(PurpleCar,215,115, {scale:0.5,rotate:3});
//setTimeout(displayPopup, 3000);
}
}
setInterval(draw ,10);
function moveEnemyCar(){
if(gameStatus == GAMEPLAYING){
enemyPositonY = enemyPositonY + 10;
if((enemyPositonY > 200)){
enemyPositonY = 30;
moveEnemyPosition();
}
}
}
setInterval(moveEnemyCar,10);
setWatch(() => {
if(gameStatus == GAMESTART){
gameStatus = GAMEPLAYING;
collision();
enemyPositonY = 0;
score = 0;
}else if(gameStatus == GAMEOVER){
gameStatus = GAMEPLAYING;
collision();
enemyPositonY = 0;
numberofHearts = 3;
score = 0;
}
}, BTN2, {repeat:true});

BIN
apps/carcrazy/carcrash.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

1
apps/nixie/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: New App!

17
apps/nixie/README.md Normal file
View File

@ -0,0 +1,17 @@
## Nixie clock
This clock displays the time in nixie-inspired numerals and works on both Bangle versions (1 and 2). It uses a generic
coordinate system (0 <= width < 1) and has helper functions to use inline.
The app makes use of a module called "m_vatch" which manages all the timers, and makes calls to functions in the 'main' file
to manage drawing the background, time, and any data like sensor info, step counters, battery, etc. The idea is that it is
reusable if you write many watch apps... you just need to implement functions to draw the background (called on start, and every
time the 'mode' changes (regular and night mode), the time (which gets a call every second), and the data (also every second,
except not in night mode)).
Night mode is a mode that can be set manually or automatically, allowing the watch code to adjust colors and detail. Mainly,
used as a night clock, you can draw no background, and use dim colors for your digits. If set to auto, the accelerometer is used so
when the watch is placed on its side, it switches to night mode (your watch may need a tweak... and Bangle 2 is a different story!)
It also handles step counting so that it's stored on a daily
basis - survives a system reset, zeroes when the date changes and keeps a record in a history file by day.

1
apps/nixie/app-icon.js Normal file
View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwkBIf4A/AH4A/AH4AtgtVAANQAwIFCAwYTGBIQDBqsF6AXCqFQroXDBQQXFAxNUBRAQKAAXVAYUEFBY7EBgtVC5UEDotdERwQBC4pGDTgQXJgoRDMoQGCqisFR5pICDQQwDCAxVJZAYXKQo7nEC6AtBCYgXGCYYDCJQYXBF5ThDKwoNCB4UMC4yyBToIGDAYNUDoRiBO5CyBLwi5Ea4RyGAH4A/AH4A/AH4A/ACQ="))

429
apps/nixie/app.js Normal file
View File

@ -0,0 +1,429 @@
const EMULATOR = false;
// which Bangle?
const isB2 = g.getWidth() < 200;
// global coordinate system
const wX = g.getWidth();
const wY = g.getHeight();
const midX = wX/2, midY = wY/2;
// relative positioning: send 0 <= coord < 1
function relX(x) { return Math.floor(x*wX); }
function relY(y) { return Math.floor(y*wY); }
// colors
const col_bg = 0;
const col_nm =isB2 ? 1 :"#206040";
const col_sep = isB2 ? 6 :"#202020";
const col_off = isB2 ? 1 : "#202020";
const col_shad1 = isB2 ? 4 :"#FF0000";
const col_shad2 = isB2 ? 6 :"#FF6000";
const col_hi =isB2 ? 7 : "#FFC000";
const col_data = isB2 ? 6 :"#C06000";
g.setBgColor(col_bg);
g.clear();
var imgTube = {
width : 64, height : 128, bpp : 2,
buffer : require("heatshrink").decompress(atob("AE9AB7sQD54AOiFQB5tVsgPN0uoBxkByEFB5kGyIPNhVVB5tpLwKAMoJuOgNQggMJgtVDhsVqtEZ5cVrWlEBcFtWq1WlJxUaBwOq1IgJgIdCqoABEBEC1WVBwTkGKgUGFYIOCgIRDC4kaFoVUOQQKCQ4IgCB4YKDCYIgCq2QgEqHwJLIEoOkgFqB4KaIEoNkB4Z7JHQVqquqD5EVDYQPCVRIPE1IPKgsAtJTCAA8GyEBD4TrKqAPOgNRB5sRB5wfPgAPOiA/RP4IPaiD6BB5oCBB5kAdQIPNH5wPCvIPMBgIPMR4QPcL4QPNgIPQvS/MqtAB59+B9cVB91VL91BF91RF79RB4OVD5wPsH59BB51FB5sQB/0AD7xvPV4elD5wPLqIPOgJPeX/6//X8YPMH5wPPL74PfN55PQB6TfPB5afDB51/D57P/Z/7P/B97vOB5kAB58VoAA="))
};
var imgTubeBW = {
width : 46, height : 92, bpp : 1,
buffer : require("heatshrink").decompress(atob("AD0EAomAAgcCBQkQEykwAgcP/gFD/wKECok4AgcB4A7DgwQEjAFEsYWExg2DhkgAoVAE4kA8AEDgZqEhw+JgA+DCwIKEhhrJCyJELFqBbQIiByLIk6gWZyC3WOSItWOVq3nCywA="))
};
require("Font8x12").add(Graphics);
g.setFont("8x12", 1);
let interval = null;
let alarming = false;
let nightMode = false;
// our scale factor
let xs = 0.5 * wX/240;
let ys = 0.75 * wY/240;
let prevH1 = -1;
let prevH2 = -1;
let prevM1 = -1;
let prevM2 = -1;
let points0 = new Uint8Array([
0, 40,
1, 35,
7, 20,
16, 8,
28, 2,
40, 0,
51, 2,
63, 10,
72, 20,
77, 35,
78, 40,
78, 59,
77, 64,
72, 79,
63, 89,
51, 97,
40, 99,
28, 97,
16, 91,
7, 79,
1, 64,
0, 59,
0, 40
]);
let points1 = new Uint8Array([ 40, 99, 40, 0]);
let points2 = new Uint8Array([ 0, 25,
2, 22,
6, 13,
17, 5,
28, 2,
40, 0,
52, 2,
63, 5,
74, 13,
79, 23,
79, 28,
74, 38,
63, 46,
51, 54,
40, 58,
29, 62,
17, 68,
8, 80,
0, 99,
79, 99
]);
let points4 = new Uint8Array([ 60, 99, 60, 0, 0, 75, 79, 75 ]);
let points8 = new Uint8Array([
40, 40,
26, 42,
15, 46,
4, 56,
1, 66,
1, 77,
6, 87,
17, 94,
28, 97,
38, 99,
42, 99,
52, 97,
63, 94,
74, 87,
79, 77,
79, 66,
75, 56,
64, 46,
54, 42,
40, 40,
52, 39,
62, 34,
69, 29,
72, 23,
72, 19,
69, 12,
62, 6,
52, 2,
40, 0,
28, 2,
18, 6,
11, 12,
8, 19,
8, 23,
11, 29,
18, 34,
28, 39,
40, 40,
]);
let points6 = new Uint8Array([
50, 0,
4, 56,
1, 66,
1, 77,
6, 87,
17, 94,
28, 97,
40, 99,
52, 97,
63, 94,
74, 87,
79, 77,
79, 66,
75, 56,
64, 46,
52, 42,
40, 40,
26, 42,
15, 46,
4, 56,
]);
let points3 = new Uint8Array([
1, 77,
6, 87,
17, 94,
28, 97,
40, 99,
52, 97,
63, 94,
74, 87,
79, 77,
79, 66,
75, 56,
64, 46,
52, 42,
39, 40,
79, 0,
1, 0
]);
let points7 = new Uint8Array([ 0, 0, 79, 0, 30, 99 ]);
let points9 = new Uint8Array(points6.length);
let points5 = new Uint8Array([
1, 77,
6, 87,
17, 94,
28, 97,
38, 99,
42, 99,
52, 97,
63, 94,
74, 87,
79, 77,
79, 66,
75, 56,
64, 46,
54, 42,
40, 40,
26, 42,
15, 46,
27, 0,
79, 0,
]);
function drawPoints(points, x0, y0) {
let x = points[0]*xs+x0, y = points[1]*ys+y0;
//g.drawEllipse(x-2, y-2, x+2, y+2);
g.moveTo(x, y);
for(let idx=1; idx*2 < points.length; idx ++) {
let x = points[idx*2]*xs+x0;
let y = points[idx*2+1]*ys+y0;
//g.drawEllipse(x-2, y-2, x+2, y+2);
g.lineTo(x, y);
}
}
/* create 5 from 2 */
/* uncomment if you want the 5 to look more authentic (but uglier)
for (let idx=0; idx*2 < points2.length; idx++) {
points5[idx*2] = points2[idx*2];
points5[idx*2+1] = 99-points2[idx*2+1];
}
*/
/* create 9 from 6 */
for (let idx=0; idx*2 < points6.length; idx++) {
points9[idx*2] = 79-points6[idx*2];
points9[idx*2+1] = 99-points6[idx*2+1];
}
pointsArray = [points0, points1, points2, points3, points4, points5, points6, points7, points8, points9];
function eraseDigit(d, x, y) {
if(d < 0 || d > 9) return;
g.setColor(col_bg);
if(nightMode) {
drawPoints(pointsArray[d], x, y);
return;
}
drawPoints(pointsArray[d], x-2, y-2);
drawPoints(pointsArray[d], x+2, y-2);
drawPoints(pointsArray[d], x-2, y+2);
drawPoints(pointsArray[d], x+2, y+2);
drawPoints(pointsArray[d], x-1, y-1);
drawPoints(pointsArray[d], x+1, y-1);
drawPoints(pointsArray[d], x-1, y+1);
drawPoints(pointsArray[d], x+1, y+1);
}
function drawDigit(d, x, y) {
if(nightMode) {
g.setColor(col_nm);
drawPoints(pointsArray[d], x, y);
return;
}
g.setColor(col_off);
for (let idx = pointsArray.length - 1; idx >= 0 ; idx--) {
if(idx == d) {
g.setColor(col_shad1);
drawPoints(pointsArray[d], x-2, y-2);
drawPoints(pointsArray[d], x+2, y-2);
drawPoints(pointsArray[d], x-2, y+2);
drawPoints(pointsArray[d], x+2, y+2);
g.setColor(col_shad2);
drawPoints(pointsArray[d], x-1, y-1);
drawPoints(pointsArray[d], x+1, y-1);
drawPoints(pointsArray[d], x-1, y+1);
drawPoints(pointsArray[d], x+1, y+1);
g.setColor(col_hi);
drawPoints(pointsArray[d], x, y);
g.setColor(col_off);
} else {
drawPoints(pointsArray[idx], x, y);
}
}
}
function drawBkgd(nm) {
g.clear();
prevH1=-1;prevH2=-1;prevM1=-1;prevM2=-1;
if(nm) return;
if(!isB2) {
// tube images
g.setColor(col_shad2);
[relX(0),relX(0.25),relX(0.5),relX(0.75)].forEach((v,i,a) => {
g.drawImage(imgTube,v,relY(0.225));
});
// something to sit on
g.setColor(col_shad2);
g.fillRect(0, relY(0.76),wX,relY(0.76));
} else {
// simple tubes
[1,45,89,133].forEach((v,i,a) => {
g.setColor(col_shad1);
g.drawEllipse(v, 52, v+41, 90);
g.drawRect(v,66,v+41,125);
g.clearRect(v+1,66,v+40,124);
});
}
g.setColor(col_shad2);
g.moveTo(relX(0.125), 0);
g.lineTo(relX(0.25), relY(0.125));
g.lineTo(relX(0.75), relY(0.125));
g.lineTo(relX(0.875),0);
g.moveTo(relX(0.125), wY);
g.lineTo(relX(0.25), relY(0.875));
g.lineTo(relX(0.75), relY(0.875));
g.lineTo(relX(0.875), wY);
}
function drawTime(d,nm) {
const dx = [relX(0.042), relX(0.29), relX(0.55), relX(0.791)]; //[ 10, 65, 135, 190];
const dy = [relY(0.38),relY(0.38),relY(0.38),relY(0.38)];
let h1 = Math.floor(d.hour / 10);
let h2 = d.hour % 10;
let m1 = Math.floor(d.min / 10);
let m2 = d.min % 10;
if(h1 == prevH1 && h2 == prevH2 && m1 == prevM1 && m2 == prevM2) {
return;
}
nightMode = nm;
if(h1 != prevH1) {
eraseDigit(prevH1, dx[0], dy[0]);
drawDigit(h1, dx[0], dy[0]);
}
if(h2 != prevH2) {
eraseDigit(prevH2, dx[1], dy[1]);
drawDigit(h2, dx[1], dy[1]);
}
if(m1 != prevM1) {
eraseDigit(prevM1, dx[2], dy[2]);
drawDigit(m1, dx[2], dy[2]);
}
if(m2 != prevM2) {
eraseDigit(prevM2, dx[3], dy[3]);
drawDigit(m2, dx[3], dy[3]);
}
prevH1 = h1;
prevH2 = h2;
prevM1 = m1;
prevM2 = m2;
}
function drawData(d) {
if(!nightMode) {
g.setColor(col_data);
g.setFontAlign(0, -1);
g.drawString(` ${d.dow}, ${d.mon3} ${d.date} `, wX/2, relX(0.042), true);
g.setFontAlign(-1,-1);
g.drawString("STEP ", 0, relY(0.82), true);
g.drawString(`${d.steps} `,0, relY(0.875), true);
g.setFontAlign(1,-1);
g.drawString(" BTY", relX(0.999), relY(0.82), true);
g.drawString(` ${d.batt}`, relX(0.999), relY(0.875), true);
g.setFontAlign(0,-1);
g.setColor(col_shad2);
g.drawString('BANGLE.JS', wX/2, relY(0.925));
}
}
//setWatch(E.showLauncher, BTN1, {repeat:true,edge:"falling"});
if(EMULATOR) {
let d = new Date();
let hour = d.getHours();
let minute = d.getMinutes();
let h1 = Math.floor(hour / 10);
let h2 = hour % 10;
let m1 = Math.floor(minute / 10);
let m2 = minute % 10;
let data = {
h1: h1,
h2: h2,
m1: m1,
m2: m2,
hour: hour,
min: minute,
};
drawBkgd(nightMode);
drawTime(data, nightMode);
const mstr="JanFebMarAprMayJunJulAugSepOctNovDec";
const dowstr = "SunMonTueWedThuFriSat";
let month = d.getMonth();
let dow = d.getDay();
data.month = month;
data.date = d.getDate();
data.mon3 = mstr.slice(month*3,month*3+3);
data.dow = dowstr.substr(dow*3,3);
data.dateStr = data.dow + " " + data.mon3 + " " + data.date;
data.steps = 12345;
data.batt = E.getBattery() + (Bangle.isCharging() ? "+" : "");
data.charging = Bangle.isCharging();
drawData(data);
} else {
Bangle.setUI("clock");
let v = require("m_vatch.js");
v.setDrawTime(drawTime);
v.setDrawBackground(drawBkgd);
v.setDrawData(drawData);
v.begin();
}

317
apps/nixie/m_vatch.js Normal file
View File

@ -0,0 +1,317 @@
const _Storage = require('Storage');
let interval = null;
let nightMode = false;
let stepFile = 'v.steps.json';
let stepArchiveFile = 'v.stephist.json';
let _Options = {};
let optsFile = 'm_vatch.opts.json';
let _Alarm = {
inAlarm: false,
reload: () => {},
scheduleAlarms: () => {},
showMsg: (title, msg) => {},
showNotes: () => {},
};
let _StepData = {};
const pad0 = (n) => (n > 9) ? n : ("0"+n);
const getToday = () => {
let d = new Date();
return d.getFullYear()+'-'+ pad0(d.getMonth()+1) + '-' + pad0(d.getDate());
};
function reload() {
_StepData = _Storage.readJSON(stepFile);
if(!_StepData) {
_StepData = {
lastDate: '2020-01-01',
stepCache: 0,
lastStepCount: 0,
updated: true,
};
}
if(getToday() === _StepData.lastDate) {
_StepData.stepCache += _StepData.lastStepCount;
_StepData.lastStepCount = 0;
}
}
function stringFromArray(data)
{
var count = data.length;
var str = "";
for(var index = 0; index < count; index += 1)
str += String.fromCharCode(data[index]);
return str;
}
function logD(str) {
if(_Options.debug) console.log(str);
}
let lastH1 = -1;
let lastH2 = -1;
let lastM1 = -1;
let lastM2 = -1;
let drawBackground = () => {};
let drawTime = () => {};
let drawData = () => {};
function timeCheck() {
if(_Alarm.inAlarm) return;
logD('Again, ' + JSON.stringify(_Options));
logD('opt.nm = '+_Options.autoNightMode);
if(_Options.autoNightMode) {
// this may vary by Bangle.. adjust to taste
let a = Bangle.getAccel();
a.x = Math.floor(a.x * 100);
logD('a.x = ' + a.x);
if(a.x <= 101 && a.x >= 99) {
if(!nightMode) {
nightMode = ! nightMode;
redrawScreen();
}
} else {
if(nightMode) {
nightMode = ! nightMode;
redrawScreen();
}
}
}
let d = new Date();
let hour = d.getHours();
let minute = d.getMinutes();
let h1 = Math.floor(hour / 10);
let h2 = hour % 10;
let m1 = Math.floor(minute / 10);
let m2 = minute % 10;
logD("lastH1 = "+lastH1+": lastM2 = "+lastM2);
if(h1 == lastH1 && h2 == lastH2 && m1 == lastM1 && m2 == lastM2) {
return;
}
logD("drawing time");
let data = {
h1: h1,
h2: h2,
m1: m1,
m2: m2,
hour: hour,
min: minute,
};
drawTime(data, nightMode);
lastH1 = h1;
lastH2 = h2;
lastM1 = m1;
lastM2 = m2;
if(!nightMode && !_Alarm.inAlarm) {
logD("drawing data...");
const mstr="JanFebMarAprMayJunJulAugSepOctNovDec";
const dowstr = "SunMonTueWedThuFriSat";
let month = d.getMonth();
let dow = d.getDay();
data.month = month;
data.date = d.getDate();
data.mon3 = mstr.slice(month*3,month*3+3);
data.dow = dowstr.substr(dow*3,3);
data.dateStr = data.dow + " " + data.mon3 + " " + data.date;
data.steps = _StepData.stepCache + _StepData.lastStepCount;
data.batt = E.getBattery() + (Bangle.isCharging() ? "+" : "");
data.charging = Bangle.isCharging();
drawData(data);
}
if(_StepData.updated) {
_Storage.writeJSON(stepFile, _StepData);
logD(JSON.stringify(_StepData));
_StepData.updated = false;
}
}
function stop () {
if (interval) {
clearInterval(interval);
}
}
function start () {
if (interval) {
clearInterval(interval);
}
// first time init
interval = setInterval(timeCheck, 1000);
timeCheck();
}
function btn1Func() {
logD("btn1Func");
if(_Alarm.inAlarm ) {
_Alarm.inAlarm = false;
} else {
if( ! _Options.autoNightMode) {
nightMode = ! nightMode;
logD('nm is '+nightMode);
}
}
redrawScreen();
}
function redrawScreen() {
logD("redrawScreen");
if(nightMode) {
g.setRotation(1,0);
} else {
g.setRotation(0,0);
}
lastM1 = -1;
lastM2 = -1;
lastH1 = -1;
lastH2 = -1;
drawBackground(nightMode);
timeCheck();
}
function btn2Func() {
_Alarm.reload();
_Alarm.scheduleAlarms();
_Alarm.showNotes();
}
Bangle.on('step', function(cnt) {
if(!_StepData.lastDate) return;
if(_StepData.lastDate !== getToday()) {
// save previous day's step count
try {
let sf = _Storage.readJSON(stepArchiveFile);
if(!sf) sf = [];
logD('sf is '+ (typeof sf) +':'+sf);
// trim to 30
if(sf.length >= 30 ) sf.shift();
let steps = _StepData.stepCache +_StepData.lastStepCount;
let sd = `${_StepData.lastDate},${steps}`;
sf.push(sd);
_Storage.writeJSON(stepArchiveFile, sf);
} catch (err) {
_Storage.write('err.txt',err);
}
/* individual step files by date
_Storage.write(_StepData.lastDate +'.steps', JSON.stringify(
_StepData.stepCache +_StepData.lastStepCount
));
*/
_StepData.stepCache = 0 - cnt;
_StepData.lastDate = getToday();
}
_StepData.lastStepCount = cnt;
_StepData.updated = true;
});
/*
** Advertise a writeable characteristic. Accepts text (in 20 char
** chunks) terminated with __EOM__ by itself. If there's text, show
** it (as an alarm), otherwise reload the alarm & msg files (empty
** string signals another BLE process updated those files)
*/
/*
var BLEMessage = "";
NRF.setServices({
"feb10001-f00d-ea75-7192-abbadabadebb": {
"feb10002-f00d-ea75-7192-abbadabadebb": {
value : [0],
maxLen : 20,
writable : true,
onWrite : function(evt) {
let str = stringFromArray(evt.data);
if(str === "__EOM__") {
if(BLEMessage) {
showMsg('Message',BLEMessage);
} else {
reload();
scheduleAlarms();
showMsg('', 'Reloading...');
}
BLEMessage = '';
} else {
BLEMessage += str;
}
}
}
}
}, { });
*/
exports.setDrawBackground = function(dBkgd) {
drawBackground = dBkgd;
};
exports.setDrawTime = function(dTime) {
drawTime = dTime;
};
exports.setDrawData = function( dData) {
drawData = dData;
};
exports.begin = function() {
_Options = _Storage.readJSON(optsFile);
if(!_Options) _Options = {
autoNightMode: true,
useAlarms: false,
stepManager: true,
debug: true,
};
console.log(JSON.stringify(_Options));
if(_Options.useAlarms) {
_Alarm = require('m_alarms');
_Alarm.reload();
_Alarm.scheduleAlarms();
}
// separate the Bangles now
const isB2 = g.getWidth() < 200;
if(!isB2) {
Bangle.on('lcdPower', function (on) {
if (on) {
start();
} else {
stop();
}
});
setWatch(btn1Func, BTN1, {repeat:true,edge:"falling"});
if(_Options.useAlarms) {
setWatch(btn2Func, BTN2, {repeat:true,edge:"falling"});
}
setWatch(Bangle.showLauncher, BTN3, {repeat:false,edge:"falling"});
}
reload();
drawBackground(nightMode);
start();
};

10
apps/nixie/nixie.info Normal file
View File

@ -0,0 +1,10 @@
{
"id":"jvNixie",
"name":"Nixie Clock",
"type":"clock",
"src":"nixie.app.js",
"icon": "nixie.img",
"sortorder":1,
"version":"1.1",
"files":"nixie.info,nixie.app.js,nixie.img, m_vatch.js"
}

BIN
apps/nixie/nixie.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 401 B