Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/schedclock/ChangeLog
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.01: New App!
36 changes: 36 additions & 0 deletions apps/schedclock/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Schedule Clock Faces

Change clock faces on a schedule.

For example: a fun clock face for weekends and after work; a detailed clock face for work days.

![Screenshot](screenshot1.png)

## Usage

* Open the `Schedule Clock` app or find it in the `Settings` > `Apps` menu.
* Set `Enabled` to checked
* Select `Add New` to add a new scheduled face change
* Select the `Day`, `Hour`, `Minute`, and what `Clock` to change to
* Select `Save` to save the new (or changed) schedule

![SaveButton](screenshot2.png)

An entry in `Scheduler` will be created for each scheduled clock change.

If the clockface you selected has been uninstalled, the schedule will still exist but won't do anything.

## To Uninstall
Before uninstalling this app, clean up any scheduled alarms by setting the `Enabled` toggle to unchecked.

If you skip this step, orphaned alarms may cause error logs but won't affect functionality.

You can also remove the extra `schedclock` alarms manually with the [Scheduler](/?id=sched) app.

## Creator

[kidneyhex](https://github.com/kidneyhex)

## Attribution

App icon: [Schedule](https://icons8.com/icon/E7VlDozxin8k/schedule) by [Icons8](https://icons8.com/)
1 change: 1 addition & 0 deletions apps/schedclock/app-icon.js

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

6 changes: 6 additions & 0 deletions apps/schedclock/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
(function () {

// Load the settings page
eval(require("Storage").read("schedclock.settings.js"))(()=>load());

})();
Binary file added apps/schedclock/app.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
84 changes: 84 additions & 0 deletions apps/schedclock/lib.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
const SETTINGS_FILE = "schedclock.settings.json";
const APP_ID = "schedclock";

/**
* Called directly by an alarm to load a specific clock face
* @param {string} faceSrc - Source file of the clock face to load (e.g. "myclock.js")
**/
const setClock = function(faceSrc) {
const settings = require("Storage").readJSON("setting.json", 1) || {};
// Only change the clock if it's different
if (faceSrc && settings.clock !== faceSrc) {
const face = require("Storage").read(faceSrc);
// If the face doesn't exist, do nothing (but log it)
if (!face) {
console.log("schedclock: Invalid clock face", faceSrc);
return;
}
settings.clock = faceSrc;
settings.clockHasWidgets = face.includes("Bangle.loadWidgets");
require("Storage").writeJSON("setting.json", settings);
if(Bangle.CLOCK) load(); // Reload clock if we're on it
}
};

/**
* Handle alarms and resetting them
* @param {number} index Index of the alarm that went off
* @param {string} clock Clockface
*/
exports.onAlarm = function(index, clock) {
const date = new Date();
const Sched = require("sched");
const alarm = Sched.getAlarm(`${APP_ID}.${index}`);
alarm.last = date.getDate(); // prevent second run on the same day
Sched.setAlarm(alarm.id, alarm);
setClock(clock);
};

/**
* Function to sync all alarms in the scheduler with the settings file.
* Called every time settings are changed; maybe a bit excessive, but keeps things simple.
**/
exports.syncAlarms = function() {
const Sched = require("sched");
const settings = require("Storage").readJSON(SETTINGS_FILE, 1) || [];

// Remove all existing alarms from the scheduler library
Sched
.getAlarms()
.filter(a => a.appid && a.appid === APP_ID)
.forEach(a => Sched.setAlarm(a.id, undefined));

// If the app is disabled, we're done.
if (!settings.enabled) return;

// Alarms need "last" set to let sched know they've already ran for the day
// So if an alarm is for before "now", set last to yesterday so it still triggers today
// else set last to today.
const currentDate = new Date();
const currentTime = (currentDate.getHours()*3600000)+(currentDate.getMinutes()*60000)+(currentDate.getSeconds()*1000);
const dayOfMonthToday = currentDate.getDate();
const dayOfMonthYesterday = dayOfMonthToday - 1;

// Add a new alarm for each setting item
settings.sched.forEach((item, index) => {

// Skip invalid records
if (item.hour === undefined || item.minute === undefined) return;

const scheduledTime = (item.hour * 3600000) + (item.minute * 60000);

// Create the new alarm object and save it using a unique ID.
Sched.setAlarm(`${APP_ID}.${index}`, {
t: scheduledTime, // time in milliseconds since midnight
on: true,
rp: true,
last: (scheduledTime > currentTime) ? dayOfMonthYesterday : dayOfMonthToday,
dow: item.dow,
hidden: true,
appid: APP_ID,
js: `require('${APP_ID}.lib.js').onAlarm(${index},'${item.face}')`,
});
});
};
23 changes: 23 additions & 0 deletions apps/schedclock/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{ "id": "schedclock",
"name": "Schedule Clock Faces",
"shortName":"Sched Clock",
"version":"0.01",
"author": "kidneyhex",
"description": "Change clock faces on a schedule.",
"icon": "app.png",
"screenshots": [
{"url":"screenshot1.png"},
{"url":"screenshot2.png"}
],
"tags": "tool",
"supports" : ["BANGLEJS2"],
"readme": "README.md",
"dependencies": {"scheduler":"type"},
"storage": [
{"name":"schedclock.app.js","url":"app.js"},
{"name":"schedclock.settings.js","url":"settings.js"},
{"name":"schedclock.lib.js","url":"lib.js"},
Copy link
Collaborator

@thyttan thyttan Sep 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
{"name":"schedclock.lib.js","url":"lib.js"},
{"name":"schedclock","url":"lib.js"},

I'm more used to this suggested pattern for loading libraries to the watch. But I see there are also apps that do it like you have it here (ha, timerclk, tinyheads, xxlmessage).

Note: If you want to change there would need to be accommodating changes at other places in the app as well.

@gfwilliams do we have a preference for how to name libraries on the watch?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side question: Is there a recommended way to migrate old settings?

I just tested adding a {"url": "migrate_v01_v02.js", "evaluate":true} to the metadata.json, and as long as I put a console.log("OK\r"); at the end of the migrate.js, it worked. But I can't find anything in docs/discussions about it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we have a preference for how to name libraries on the watch?

If they're being exported from the app, definitely just schedclock - but in this case I think it's just being used internally, so naming it schedclock.lib.js seems fine - maybe even preferable?

{"name":"schedclock.img","url":"app-icon.js","evaluate":true}
],
"data": [{"name":"schedclock.settings.json"}]
}
Binary file added apps/schedclock/screenshot1.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/schedclock/screenshot2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading