diff --git a/apps/ateatimer/ChangeLog b/apps/ateatimer/ChangeLog new file mode 100644 index 0000000000..81da9fdce9 --- /dev/null +++ b/apps/ateatimer/ChangeLog @@ -0,0 +1,2 @@ +0.01: First release +0.02: Fix icon, utilize sched, show running timer on app relaunch \ No newline at end of file diff --git a/apps/ateatimer/app-icon.js b/apps/ateatimer/app-icon.js new file mode 100644 index 0000000000..f80208eada --- /dev/null +++ b/apps/ateatimer/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwgIKHgwFKo0gAofmsALEGR0H/+f//+gEP/4ACAoXAn4FDAQn8g0DAoX4g0BAoXx4E4AoXhAoN/8EP4AzBn/4h/IC4M//kPzgjBz/+h+MAoMfj0PNYUfh4FDh8HAo0wg/454RBmBDBAoRnBCIIjCAAMPF4IFDHYOIgEBj5HBzkAIIPAIIIFBn4hBLIU+AoPgwEQvwFBOIX8CgP5w0RAoSJC/AsB/0EJwIgB/+Aj/wAoN/VgPgQwQFBwBKCXAQWBAAfgAoocCAoQcCAAPAj7XEcYIABcYLIBAAJBBA==")) \ No newline at end of file diff --git a/apps/ateatimer/app.js b/apps/ateatimer/app.js new file mode 100644 index 0000000000..9322d4e466 --- /dev/null +++ b/apps/ateatimer/app.js @@ -0,0 +1,156 @@ +// Tea Timer Application for Bangle.js 2 using sched library + +let timerDuration = (() => { + let file = require("Storage").open("ateatimer.data", "r"); + let data = file.read(4); // Assuming 4 bytes for storage + return data ? parseInt(data, 10) : 4 * 60; // Default to 4 minutes +})(); +let timeRemaining = timerDuration; +let timerRunning = false; + +function saveDefaultDuration() { + let file = require("Storage").open("ateatimer.data", "w"); + file.write(timerDuration.toString()); +} + +function drawTime() { + g.clear(); + g.setFont("Vector", 40); + g.setFontAlign(0, 0); // Center align + + const minutes = Math.floor(Math.abs(timeRemaining) / 60); + const seconds = Math.abs(timeRemaining) % 60; + const sign = timeRemaining < 0 ? "-" : ""; + const timeStr = `${sign}${minutes}:${seconds.toString().padStart(2, '0')}`; + + g.drawString(timeStr, g.getWidth() / 2, g.getHeight() / 2); + + // Draw Increase button (triangle pointing up) + g.fillPoly([ + g.getWidth() / 2, g.getHeight() / 2 - 80, // Top vertex + g.getWidth() / 2 - 20, g.getHeight() / 2 - 60, // Bottom-left vertex + g.getWidth() / 2 + 20, g.getHeight() / 2 - 60 // Bottom-right vertex + ]); + + // Draw Decrease button (triangle pointing down) + g.fillPoly([ + g.getWidth() / 2, g.getHeight() / 2 + 80, // Bottom vertex + g.getWidth() / 2 - 20, g.getHeight() / 2 + 60, // Top-left vertex + g.getWidth() / 2 + 20, g.getHeight() / 2 + 60 // Top-right vertex + ]); + + g.flip(); +} + +function startTimer() { + if (timerRunning) return; + if (timeRemaining == 0) return; + timerRunning = true; + + // Save the default duration on timer start + timerDuration = timeRemaining; + saveDefaultDuration(); + scheduleTimer(); + + // Start the secondary timer to update the display + setInterval(updateDisplay, 1000); +} + +function scheduleTimer() { + // Schedule a new timer using the sched library + require("sched").setAlarm("ateatimer", { + msg: "Tea is ready!", + timer: timeRemaining * 1000, // Convert to milliseconds + vibrate: ".." // Default vibration pattern + }); + + // Ensure the scheduler updates + require("sched").reload(); +} + +function resetTimer() { + // Cancel the existing timer + require("sched").setAlarm("ateatimer", undefined); + require("sched").reload(); + + timerRunning = false; + timeRemaining = timerDuration; + drawTime(); +} + +function adjustTime(amount) { + if (-amount > timeRemaining) { + // Return if result will be negative + return; + } + timeRemaining += amount; + timeRemaining = Math.max(0, timeRemaining); // Ensure time doesn't go negative + if (timerRunning) { + // Update the existing timer with the new remaining time + let alarm = require("sched").getAlarm("ateatimer"); + if (alarm) { + // Cancel the current alarm + require("sched").setAlarm("ateatimer", undefined); + + // Set a new alarm with the updated time + scheduleTimer(); + } + } + + drawTime(); +} + +function handleTouch(x, y) { + const centerY = g.getHeight() / 2; + + if (y < centerY - 40) { + // Increase button area + adjustTime(60); + } else if (y > centerY + 40) { + // Decrease button area + adjustTime(-60); + } else { + // Center area + if (!timerRunning) { + startTimer(); + } + } +} + +// Function to update the display every second +function updateDisplay() { + if (timerRunning) { + let alarm = require("sched").getAlarm("ateatimer"); + timeRemaining = Math.floor(require("sched").getTimeToAlarm(alarm) / 1000); + drawTime(); + if (timeRemaining <= 0) { + timeRemaining = 0; + clearInterval(updateDisplay); + timerRunning = false; + } + } +} + +// Handle physical button press for resetting timer +setWatch(() => { + if (timerRunning) { + resetTimer(); + } else { + startTimer(); + } +}, BTN1, { repeat: true, edge: "falling" }); + +// Handle touch +Bangle.on("touch", (zone, xy) => { + handleTouch(xy.x, xy.y, false); +}); + +let isRunning = require("sched").getAlarm("ateatimer"); +if (isRunning) { + timerRunning = true; + // Start the timer to update the display + setInterval(updateDisplay, 1000); +} else { + // Draw the initial timer display + drawTime(); +} \ No newline at end of file diff --git a/apps/ateatimer/app.json b/apps/ateatimer/app.json new file mode 100644 index 0000000000..7304a3d426 --- /dev/null +++ b/apps/ateatimer/app.json @@ -0,0 +1 @@ +{ "duration": 240 } \ No newline at end of file diff --git a/apps/ateatimer/app.png b/apps/ateatimer/app.png new file mode 100644 index 0000000000..4c25f7d33b Binary files /dev/null and b/apps/ateatimer/app.png differ diff --git a/apps/ateatimer/metadata.json b/apps/ateatimer/metadata.json new file mode 100644 index 0000000000..c4b8a14584 --- /dev/null +++ b/apps/ateatimer/metadata.json @@ -0,0 +1,14 @@ +{ "id": "ateatimer", + "name": "A Tea Timer", + "shortName":"A Tea Timer", + "icon": "app.png", + "version":"0.02", + "description": "Simple app for setting timers for tea. Touch up and down to change time, and time or button to start counting. When timer is running, button will stop timer and reset counter to last used value.", + "tags": "timer", + "supports": ["BANGLEJS2"], + "storage": [ + {"name":"ateatimer.app.js","url":"app.js"}, + {"name":"ateatimer.img","url":"app-icon.js","evaluate":true} + ], + "dependencies": {"scheduler":"type"} +}