Skip to content

Commit

Permalink
Merge pull request #3448 from slynchDev/master
Browse files Browse the repository at this point in the history
Burn - Simple Calorie Counter
  • Loading branch information
thyttan authored Jun 9, 2024
2 parents 13e2527 + 6cc24ef commit 21825d9
Show file tree
Hide file tree
Showing 8 changed files with 298 additions and 0 deletions.
1 change: 1 addition & 0 deletions apps/burn/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.prettierignore
7 changes: 7 additions & 0 deletions apps/burn/ChangeLog
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
0.01: New App!
0.02: Added README.md
0.03: Icon update
0.04: Icon Fix
0.05: Misc cleanup for PR
0.06: Implementing fixes from PR comments
0.07: Bug fix
30 changes: 30 additions & 0 deletions apps/burn/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Burn: Calorie Counter

Burn is a simple calorie counter application. It is based on the original Counter app and has been enhanced with additional features (I recommend using
it with the "Digital Clock Widget", if you intend to keep it running).

## Features

- **Persistent counter**: The counter value is saved to flash, so it persists even when the app is closed or the device is restarted.
- **Daily reset**: The counter resets each day, allowing you to track your calorie intake on a daily basis.
- **Adjustable increment value**: You can adjust the increment value to suit your needs.

## Controls

### Bangle.js 1

- **BTN1**: Increase (or tap right)
- **BTN3**: Decrease (or tap left)
- **Press BTN2**: Change increment

### Bangle.js 2

- **Swipe up**: Increase
- **Swipe down**: Decrease
- **Press BTN**: Change increment

## How it Works

The counter value and the date are stored in a file named "kcal.txt". The counter value is read from the file when the app starts and written to the file whenever the counter value is updated.

The app uses the current date to determine whether to reset the counter. If the date has changed since the last time the counter was updated, the counter is reset to 0.
1 change: 1 addition & 0 deletions apps/burn/app-icon.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file added apps/burn/app-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/burn/app-screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
243 changes: 243 additions & 0 deletions apps/burn/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
/*
* Burn: Calories Counter for Bangle.js (Espruino). Based on the original Counter app.
* Features:
* - Persistent counter: saved to a file.
* - Daily reset: counter resets each day.
* - Adjustable increment value.
*
* Bangle.js 1 Controls:
* - BTN1: Increase (or tap right)
* - BTN3: Decrease (or tap left)
* - Press BTN2: Change increment
*
* Bangle.js 2 Controls:
* - Swipe up: Increase
* - Swipe down: Decrease
* - Press BTN: Change increment
*/

// File variable to handle file operations
let file;

// Check if the hardware version is Bangle.js 2
const BANGLEJS2 = process.env.HWVERSION == 2;

// Importing the Storage module for file operations
const Storage = require("Storage");

// File path for the counter data
const PATH = "kcal.txt";

// Function to get the current date as a string
function dayString() {
const date = new Date();
// Month is 0-indexed, so we add 1 to get the correct month number
return `${date.getMonth() + 1}-${date.getDate()}-${date.getFullYear()}`;
}

// Counter object to keep track of the count and the date
let counter = { count: 0, date: dayString() };

// Function to read the counter from the file
function readCounterFromFile() {
try {
// Open the file in read mode
file = Storage.open(PATH, "r");
let line = file.readLine();

// If the file has content, parse it and update the counter
if (line) {
let splitLine = line.trim().split(",");
counter = { count: parseInt(splitLine[0]), date: splitLine[1] };
}
} catch (err) {
// If the file does not exist, the counter will remain 0
}
}

// Function to write the counter to the file
function writeCounterToFile() {
// Open the file in write mode
file = Storage.open(PATH, "w");
// Write the counter and date to the file
file.write(counter.count.toString() + "," + counter.date + "\n");
}

// Function to reset the counter
function resetCounter() {
// Reset the counter to 0 and update the date
counter = { count: 0, date: dayString() };
}

// Function to update the counter value
function updateCounterValue(value) {
// Update the counter with the new value, ensuring it's not less than 0
counter = { count: Math.max(0, value), date: dayString() };
}

// Function to update the counter
function updateCounter(value) {
// If the date has changed, reset the counter
if (counter.date != dayString()) {
resetCounter();
} else {
// Otherwise, update the counter value
updateCounterValue(value);
}

// Write the updated counter to the file
writeCounterToFile();
// Update the screen with the new counter value
updateScreen();
}

// Function to set a watch on a button to update the counter when pressed
function counterButtonWatch(button, increment) {
setWatch(
() => {
// If the button is for incrementing, or the counter is greater than 0, update the counter
if (increment || counter.count > 0) {
updateCounter(
counter.count + (increment ? getInterval() : -getInterval())
);
// Update the screen with the new counter value
updateScreen();
}
},
button,
{ repeat: true }
);
}

// Function to create interval functions
const createIntervalFunctions = function () {
// Array of intervals
const intervals = [50, 100, 200, 10];
// Current location in the intervals array
let location = 0;

// Function to get the current interval
const getInterval = function () {
return intervals[location];
};

// Function to rotate the increment
const rotateIncrement = function () {
// Update the location to the next index in the intervals array, wrapping around if necessary
location = (location + 1) % intervals.length;
// Update the screen with the new increment
updateScreen();
};

// Return the getInterval and rotateIncrement functions
return { getInterval, rotateIncrement };
};

// Create the interval functions
const intervalFunctions = createIntervalFunctions();
const getInterval = intervalFunctions.getInterval;
const rotateIncrement = intervalFunctions.rotateIncrement;

// Function to update the screen
function updateScreen() {
// Clear the screen area for the counter
g.clearRect(0, 50, 250, BANGLEJS2 ? 130 : 150)
.setBgColor(g.theme.bg)
.setColor(g.theme.fg)
.setFont("Vector", 40)
.setFontAlign(0, 0)
// Draw the counter value
.drawString(Math.floor(counter.count), g.getWidth() / 2, 100)
.setFont("6x8")
// Clear the screen area for the increment
.clearRect(g.getWidth() / 2 - 50, 140, g.getWidth() / 2 + 50, 160)
// Draw the increment value
.drawString("Increment: " + getInterval(), g.getWidth() / 2, 150);

// If the hardware version is Bangle.js 1, draw the increment and decrement buttons
if (!BANGLEJS2) {
g.drawString("-", 45, 100).drawString("+", 185, 100);
}
}

// If the hardware version is Bangle.js 2, set up the drag handling and button watch

let drag;

if (BANGLEJS2) {
// Set up drag handling
Bangle.on("drag", (e) => {
// If this is the start of a drag, record the initial coordinates
if (!drag) {
drag = { x: e.x, y: e.y };
return;
}

// If the button is still being pressed, ignore this event
if (e.b) return;

// Calculate the change in x and y from the start of the drag
const dx = e.x - drag.x;
const dy = e.y - drag.y;
// Reset the drag start coordinates
drag = null;

// Determine if the drag is primarily horizontal or vertical
const isHorizontalDrag = Math.abs(dx) > Math.abs(dy) + 10;
const isVerticalDrag = Math.abs(dy) > Math.abs(dx) + 10;

// If the drag is primarily horizontal, ignore it
if (isHorizontalDrag) {
return;
}

// If the drag is primarily vertical, update the counter
if (isVerticalDrag) {
// If the drag is downwards and the counter is greater than 0, decrease the counter
if (dy > 0 && counter.count > 0) {
updateCounter(counter.count - getInterval());
} else if (dy < 0) {
// If the drag is upwards, increase the counter
updateCounter(counter.count + getInterval());
}
// Update the screen with the new counter value
updateScreen();
}
});

// Set a watch on the button to rotate the increment when pressed
setWatch(rotateIncrement, BTN1, { repeat: true });
} else {
// If the hardware version is Bangle.js 1, set up the button watches

// Set watch on button to increase the counter
counterButtonWatch(BTN1, true);
counterButtonWatch(BTN5, true); // screen tap
// Set watch on button to decrease the counter
counterButtonWatch(BTN3, false);
counterButtonWatch(BTN4, false); // screen tap

// Set a watch on button to rotate the increment when pressed
setWatch(
() => {
rotateIncrement();
},
BTN2,
{ repeat: true }
);
}

// clear the screen
g.clear();

// Set the background and foreground colors
g.setBgColor(g.theme.bg).setColor(g.theme.fg);

// Load and draw the widgets
Bangle.loadWidgets();
Bangle.drawWidgets();

// Read the counter from the file
readCounterFromFile();
// Update the screen with the counter value
updateScreen();
16 changes: 16 additions & 0 deletions apps/burn/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"id": "burn",
"name": "Burn",
"version": "0.07",
"description": "Simple Calorie Counter -- saves to flash and resets at midnight. I often keep mine running while the digital clock widget is at the top",
"icon": "app-icon.png",
"tags": "tool",
"readme":"README.md",
"supports": ["BANGLEJS", "BANGLEJS2"],
"screenshots": [{"url":"app-screenshot.png"}],
"allow_emulator": true,
"storage": [
{"name":"burn.app.js","url":"app.js"},
{"name":"burn.img","url":"app-icon.js","evaluate":true}
]
}

0 comments on commit 21825d9

Please sign in to comment.