commit
11a7854015
|
|
@ -0,0 +1,3 @@
|
|||
0.10: Initial release - still work in progress
|
||||
0.15: Added settings and calculations
|
||||
0.20: Added status saving
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
# Drink Counter
|
||||
|
||||
Development still in progress. Counts drinks you had for science. Calculates BAC.
|
||||
|
||||
## Usage
|
||||
|
||||
Swipe left/right to select drink. Swipe up/down to add/remove drinks.
|
||||
|
||||
|
||||
## Creator
|
||||
|
||||
Hank - contact at http://forum.espruino.com
|
||||
|
|
@ -0,0 +1,271 @@
|
|||
g.reset().clear();
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
require("Font8x16").add(Graphics);
|
||||
|
||||
const SETTINGSFILE = "drinkcounter.json";
|
||||
setting = require("Storage").readJSON("setting.json",1);
|
||||
E.setTimeZone(setting.timezone); // timezone = 1 for MEZ, = 2 for MESZ
|
||||
var _12hour = (require("Storage").readJSON("setting.json",1)||{})["12hour"]||false;
|
||||
var ampm = "AM";
|
||||
let drag;
|
||||
|
||||
var icoBeer = require("heatshrink").decompress(atob("lEoxH+AG2BAAoecEpAoWC4fXAAIGGAAowTDxAmJE4YGGE5QeJE5QHHE7owJE0pQKE7pQJE86fnE5QJSE5YUHBAIJQYxIpFAAvGBBAJIExYoGDgIACBBApFExonCDYoAOFSAnbFJYnE6vVDYYFHAwakQE4YaFAoQGJEIYoME7QoEE7ogFE/4neTBgntY84n/E+7HUE64mDE8IAFEw4nDTBifIE9gmId7gALE5IGCAooGDE6gASE8yaME7gmOFIgAREqIAhA=="));
|
||||
var icoCocktail = require("heatshrink").decompress(atob("lEoxH+AH4AJtgABEkgmiEiXGAAIllAAiXeEAPXAQQDCFBYmTEgYqDFBZNWAIZRME6IfBEAYuEE5J2UwIAaJ5QncFBB3DB4YGCACQnKTQgoXE5bIEE6qfKPAZRFA4MUABgmNPAonBCgQnPExgpFPIgoNEyBSF4wGBFBgmSABCjJTZwoXEzwoHE0AoFE0QnCFAQmhKAonjFAInCE0Qn/E/4n/E/4n/wInDFEAhBEwQoDFLYdCEwooEFTAjHAAwoYIYgAMPDglT"));
|
||||
var icoShot = require("heatshrink").decompress(atob("lEoxH+AH4A/AH4A/AH4AqwIAgE+HXADRPME8ZQM5AnSZBQkGAAYngEYonfJA5QQE8zGJFAYfKFBwmKE4iYIE7rpIeYgAJE5woEEpQKHTxhQIIpJaHJxgn/E8zGQZBAnQYxxQRFQYnlFgon5FCYmDE6LjHZRQmPE5AAOE/4njFCTGQKCwmRKAgATE54oWEyAqTDZY"));
|
||||
var icoReset = require("heatshrink").decompress(atob("j0egILI8ACBh4DC/4DBh4DCv8f4ED8EPwEPEQMAvEAnkB4EA+AKBCAM8DYOA8EB//HwED/wXBg/wnAOC+EAjkDDoMgg+AJoRFCEIIAB/kHgEB/l8FwP/DYIDBC4MD/ASBgYeCAAw"));
|
||||
var drawTimeout;
|
||||
var activeDrink = 0;
|
||||
var drinks = [0,0,0];
|
||||
const maxDrinks = 2; // 3 drinks
|
||||
var firstDrinkTime = null;
|
||||
var firstDrinkTimeTime = null;
|
||||
|
||||
var confBeerSize;
|
||||
var confSex;
|
||||
var confWeight;
|
||||
var confWeightUnit;
|
||||
|
||||
|
||||
// Load Status ===============
|
||||
var drinkStatus = require("Storage").open("drinkcounter.status.json", "r");
|
||||
var test = drinkStatus.read(drinkStatus.getLength());
|
||||
if(test!== undefined) {
|
||||
drinkStatus = JSON.parse(test);
|
||||
//console.log("read status: " + test);
|
||||
for (let i = 0; i <= maxDrinks; i++) {
|
||||
drinks[i] = drinkStatus.drinks[i];
|
||||
}
|
||||
firstDrinkTime = Date.parse(drinkStatus.firstDrinkTime);
|
||||
//console.log("read firstDrinkTime: " + firstDrinkTime);
|
||||
if (firstDrinkTime) firstDrinkTimeTime = require("locale").time(new Date(firstDrinkTime), 1);
|
||||
//console.log("read firstDrinkTimeTime: " + firstDrinkTimeTime);
|
||||
} else {
|
||||
drinkStatus = {
|
||||
drinks: [0,0,0]
|
||||
};
|
||||
//console.log("no status file - applying default");
|
||||
}
|
||||
// Load Status ===============
|
||||
|
||||
|
||||
var drinksAlcohol = [12,16,5.6]; // in gramm
|
||||
// Beer: 0.3L 12g - 0.5L 20g
|
||||
// Radler: 0.3L 6g - 0.5L 10g
|
||||
// Wine: 0.2L 16g
|
||||
// Jäger Shot: 0.02L 5.6g
|
||||
|
||||
// sex: Women 60 - Men 70 (Percent)
|
||||
// Formula: Alcohol in g /(Body weight in kg x sex) – (0,15 x Hours) = bac per mille
|
||||
// Example: 5 Beer (0.3L=12g), 80KG, Male (70%), 5 hours
|
||||
// (5 * 12) / (80 / 100 * 70) - (0.15 * 5)
|
||||
|
||||
function drawBac(){
|
||||
if (firstDrinkTime) {
|
||||
var sum_drinks = (drinks[0] * drinksAlcohol[0]) + (drinks[1] * drinksAlcohol[1]) + (drinks[2] * drinksAlcohol[2]);
|
||||
|
||||
if (confSex == "male") {
|
||||
sex = 70;
|
||||
} else {
|
||||
sex = 60;
|
||||
}
|
||||
var weight = confWeight;
|
||||
|
||||
if (confWeightUnit == "US Pounds") {
|
||||
weight = weight * 0.45359237;
|
||||
}
|
||||
var currentTime = new Date();
|
||||
var time_diff = Math.floor(((currentTime - firstDrinkTime) % 86400000) / 3600000); // in hours!
|
||||
//console.log("currentTime: " + currentTime)
|
||||
//console.log("firstDrinkTime: " + firstDrinkTime)
|
||||
|
||||
//console.log("timediff: " + time_diff);
|
||||
ebac = Math.round( ((sum_drinks) / (weight / 100 * sex) - (0.15 * time_diff) ) * 100) / 100;
|
||||
|
||||
//console.log("BAC: " + ebac + " weight: " + confWeight + " weightInKilo: " + weight + " Unit: " + confWeightUnit);
|
||||
//console.log("sum_drinks: " + sum_drinks);
|
||||
g.clearRect(0,34 + 20 + 8,176,34 + 20 + 20 + 8); //Clear
|
||||
g.setFontAlign(0,0).setFont("8x16").setColor(g.theme.fg).drawString("BAC: " + ebac, 90, 74);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Load settings
|
||||
function loadMySettings() {
|
||||
// Helper function default setting
|
||||
function def (value, def) {return value !== undefined ? value : def;}
|
||||
|
||||
var settings = require('Storage').readJSON(SETTINGSFILE, true) || {};
|
||||
confBeerSize = def(settings.beerSize, "0.3L");
|
||||
confSex = def(settings.sex, "male");
|
||||
confWeight = def(settings.weight, 80);
|
||||
confWeightUnit = def(settings.weightUnit, "Kilo");
|
||||
//console.log("Read config - weight: " + confWeight);
|
||||
}
|
||||
|
||||
|
||||
function updateTime(){
|
||||
var d = require("locale").time(new Date(), 1);
|
||||
|
||||
//console.log(d);
|
||||
var time = d.split(":");
|
||||
var hours = time[0];
|
||||
var minutes = time[1];
|
||||
if (_12hour){
|
||||
//do 12 hour stuff
|
||||
if (hours > 12) {
|
||||
ampm = "PM";
|
||||
hours = hours - 12;
|
||||
if (hours < 10) hours = doublenum(hours);
|
||||
} else {
|
||||
ampm = "AM";
|
||||
}
|
||||
} else {
|
||||
ampm = "";
|
||||
}
|
||||
g.setBgColor(g.theme.bg).clearRect(0,24,176,44); //Clear
|
||||
g.setFontAlign(0,0); // center font
|
||||
g.setBgColor(g.theme.bg).setColor(g.theme.fg);
|
||||
g.setFont("8x16").drawString("Time: " + hours + ":" + minutes + " " + ampm,90,34);
|
||||
queueDrawTime();
|
||||
}
|
||||
|
||||
function queueDrawTime() {
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(function() {
|
||||
drawTimeout = undefined;
|
||||
updateTime();
|
||||
}, 20000 - (Date.now() % 20000));
|
||||
}
|
||||
|
||||
|
||||
function updateDrinks(){
|
||||
g.setBgColor(g.theme.bg).clearRect(0,145,176,176); //Clear
|
||||
for (let i = 0; i <= maxDrinks; i++) {
|
||||
if (i == activeDrink) {
|
||||
g.setColor(g.theme.fg).fillRect((40 * (i + 1)) - 40 ,145,(40 * (i + 1)),176);
|
||||
g.setColor(g.theme.bg);
|
||||
} else {
|
||||
g.setColor(g.theme.fg);
|
||||
}
|
||||
g.setFont("Vector",20).drawString(drinks[i], (40 * (i + 1)) - 20, 160);
|
||||
g.setColor(g.theme.fg);
|
||||
drinkStatus.drinks[i] = drinks[i];
|
||||
}
|
||||
|
||||
g.setBgColor(g.theme.bg).setColor(g.theme.fg);
|
||||
g.drawImage(icoReset,145,145);
|
||||
|
||||
drinkStatus.firstDrinkTime = firstDrinkTime;
|
||||
settings_file = require("Storage").open("drinkcounter.status.json", "w");
|
||||
settings_file.write(JSON.stringify(drinkStatus));
|
||||
|
||||
drawBac();
|
||||
}
|
||||
|
||||
function updateFirstDrinkTime(){
|
||||
if (firstDrinkTime){
|
||||
g.setFont("8x16");
|
||||
g.setFontAlign(0,0).drawString("1st drink @ " + firstDrinkTimeTime, 90, 34 + 20 );
|
||||
}
|
||||
}
|
||||
|
||||
function addDrink(){
|
||||
if (!firstDrinkTime){
|
||||
firstDrinkTime = new Date();
|
||||
firstDrinkTimeTime = require("locale").time(new Date(), 1);
|
||||
//console.log("init drinking! " + firstDrinkTime);
|
||||
}
|
||||
drinks[activeDrink] = drinks[activeDrink] + 1;
|
||||
updateFirstDrinkTime();
|
||||
updateDrinks();
|
||||
}
|
||||
|
||||
function removeDrink(){
|
||||
if (drinks[activeDrink] > 0) drinks[activeDrink] = drinks[activeDrink] - 1;
|
||||
updateDrinks();
|
||||
}
|
||||
|
||||
function previousDrink(){
|
||||
if (activeDrink > 0) activeDrink = activeDrink - 1;
|
||||
updateDrinks();
|
||||
}
|
||||
|
||||
function nextDrink(){
|
||||
if (activeDrink < maxDrinks) activeDrink = activeDrink + 1;
|
||||
updateDrinks();
|
||||
}
|
||||
|
||||
function showDrinks() {
|
||||
g.setBgColor(g.theme.bg);
|
||||
g.drawImage(icoBeer,0,100);
|
||||
g.drawImage(icoCocktail,40,100);
|
||||
g.drawImage(icoShot,80,100);
|
||||
}
|
||||
|
||||
function initDragEvents() {
|
||||
Bangle.on("drag", e => {
|
||||
if (!drag) { // start dragging
|
||||
drag = {x: e.x, y: e.y};
|
||||
} else if (!e.b) { // released
|
||||
const dx = e.x-drag.x, dy = e.y-drag.y;
|
||||
drag = null;
|
||||
if (Math.abs(dx)>Math.abs(dy)+10) {
|
||||
// horizontal
|
||||
if (dx < dy) {
|
||||
//console.log("left " + dx + " " + dy);
|
||||
previousDrink();
|
||||
} else {
|
||||
//console.log("right " + dx + " " + dy);
|
||||
nextDrink();
|
||||
}
|
||||
} else if (Math.abs(dy)>Math.abs(dx)+10) {
|
||||
// vertical
|
||||
if (dx < dy) {
|
||||
//console.log("down " + dx + " " + dy);
|
||||
removeDrink();
|
||||
} else {
|
||||
//console.log("up " + dx + " " + dy);
|
||||
addDrink();
|
||||
}
|
||||
} else {
|
||||
//console.log("tap " + e.x + " " + e.y);
|
||||
if (e.x > 145 && e.y > 145) {
|
||||
g.clearRect(0,34,176,176); //Clear
|
||||
resetDrinks = E.showPrompt("Reset drinks?", {
|
||||
title: "Confirm",
|
||||
buttons: { Yes: true, No: false },
|
||||
});
|
||||
resetDrinks.then((confirm) => {
|
||||
if (confirm) {
|
||||
for (let i = 0; i <= maxDrinks; i++) {
|
||||
drinks[i] = 0;
|
||||
}
|
||||
//console.log("reset to default");
|
||||
}
|
||||
//console.log("reset " + confirm);
|
||||
firstDrinkTime = null;
|
||||
showDrinks();
|
||||
updateDrinks();
|
||||
updateTime();
|
||||
updateFirstDrinkTime();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
loadMySettings();
|
||||
|
||||
showDrinks();
|
||||
|
||||
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
updateTime();
|
||||
queueDrawTime();
|
||||
initDragEvents();
|
||||
updateDrinks();
|
||||
updateFirstDrinkTime();
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwxH+AH4A/AH4A/AAWBAAomkFpAweD4fXAAIGGAAo4bExAuJF4YGGF6QmJF5QHHF8o4JF1pgSF7pgRF96/vF5QJSF6YcHBAIJQdyIxFAAvGBBAJIFyYwGEgIACBBAxFFyovCEYoAOGTAvbGKYvE6vVEYYFHAwbEYF4YiFAoQGJFIYwUF7QwEF8ooFF/4v2XBgv1d94v/F/7vsF64uDF9IAFFx4vDXBi/IF+guQR6wvCFSIvOAwQFFAwYvcACQvuXSgvcFywxEACItZAH4A/AH4AlA=="))
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 811 B |
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"id": "drinkcounter",
|
||||
"name": "Drink Counter",
|
||||
"shortName": "Drink Counter",
|
||||
"version": "0.20",
|
||||
"description": "Counts drinks you had for science. Calculates blood alcohol content (BAC)",
|
||||
"allow_emulator":true,
|
||||
"icon": "drinkcounter.png",
|
||||
"type": "app",
|
||||
"tags": "health",
|
||||
"screenshots": [{"url":"screenshot_drnkcnt.png"}],
|
||||
"supports": ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"drinkcounter.app.js","url":"app.js"},
|
||||
{"name":"drinkcounter.img","url":"drinkcounter-icon.js","evaluate":true},
|
||||
{"name":"drinkcounter.settings.js","url":"settings.js"}
|
||||
],
|
||||
"data": [
|
||||
{"name":"drinkcounter.settings.json"},
|
||||
{"name":"drinkcounter.json"},
|
||||
{"name":"drinkcounter.status.json"}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
|
|
@ -0,0 +1,58 @@
|
|||
(function(back) {
|
||||
var FILE = "drinkcounter.json";
|
||||
var settings = Object.assign({
|
||||
secondsOnUnlock: false,
|
||||
}, require('Storage').readJSON(FILE, true) || {});
|
||||
|
||||
function writeSettings() {
|
||||
require('Storage').writeJSON(FILE, settings);
|
||||
}
|
||||
|
||||
// Helper method which uses int-based menu item for set of string values
|
||||
function stringItems(startvalue, writer, values) {
|
||||
return {
|
||||
value: (startvalue === undefined ? 0 : values.indexOf(startvalue)),
|
||||
format: v => values[v],
|
||||
min: 0,
|
||||
max: values.length - 1,
|
||||
wrap: true,
|
||||
step: 1,
|
||||
onchange: v => {
|
||||
writer(values[v]);
|
||||
writeSettings();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Helper method which breaks string set settings down to local settings object
|
||||
function stringInSettings(name, values) {
|
||||
return stringItems(settings[name], v => settings[name] = v, values);
|
||||
}
|
||||
|
||||
var mainmenu = {
|
||||
"": {
|
||||
"title": "Drink counter"
|
||||
},
|
||||
"< Back": () => back(),
|
||||
|
||||
"Beer size": stringInSettings("beerSize", ["0.3L", "0.5L"]),
|
||||
|
||||
|
||||
"Sex": stringInSettings("sex", ["male", "female"]),
|
||||
|
||||
'Weight': {
|
||||
value: 80|settings.weight,
|
||||
min: 40, max: 500,
|
||||
onchange: v => {
|
||||
settings.weight = v;
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
"Weight unit": stringInSettings("weightUnit", ["Kilo", "US Pounds"])
|
||||
|
||||
|
||||
};
|
||||
|
||||
E.showMenu(mainmenu);
|
||||
|
||||
});
|
||||
Loading…
Reference in New Issue