Skip to content

Commit 1c2c6f0

Browse files
authored
Merge pull request espruino#4096 from kidneyhex/schedclock-rand
[schedclock] allow setting random faces per entry; fix 24h selection
2 parents 449d6fe + 9f92b51 commit 1c2c6f0

File tree

5 files changed

+101
-22
lines changed

5 files changed

+101
-22
lines changed

apps/schedclock/ChangeLog

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
0.01: New App!
2+
0.02: Add multiple clock faces per entry and apply one at random
3+
Fix bug handling 24h format in settings

apps/schedclock/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ If you skip this step, orphaned alarms may cause error logs but won't affect fun
2727

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

30+
## Fastload Utils
31+
32+
If you are using `Fastload Utils` (https://banglejs.com/apps/?id=fastload) it may not load the new face until the next time it does a full load.
33+
34+
You may be able to work around this by enabling FastLoad's setting to detect settings changes.
35+
3036
## Creator
3137

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

apps/schedclock/lib.js

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,32 @@ const setClock = function(faceSrc) {
2525
/**
2626
* Handle alarms and resetting them
2727
* @param {number} index Index of the alarm that went off
28-
* @param {string} clock Clockface
2928
*/
30-
exports.onAlarm = function(index, clock) {
29+
exports.onAlarm = function(index) {
3130
const date = new Date();
3231
const Sched = require("sched");
3332
const alarm = Sched.getAlarm(`${APP_ID}.${index}`);
3433
alarm.last = date.getDate(); // prevent second run on the same day
3534
Sched.setAlarm(alarm.id, alarm);
36-
setClock(clock);
35+
36+
// Look up the entry in the settings file
37+
const settings = require("Storage").readJSON(SETTINGS_FILE, 1) || {};
38+
const item = settings.sched && settings.sched[index];
39+
if (!item) {
40+
console.log("schedclock: Schedule item not found for index", index);
41+
return;
42+
}
43+
44+
// Handle both 'faces' array and legacy 'face' string for backwards compatibility
45+
const faces = item.faces || (item.face ? [item.face] : []);
46+
if (faces.length === 0) {
47+
console.log("schedclock: No clock faces found for schedule item", index);
48+
return;
49+
}
50+
51+
// Randomly select one face from the list
52+
const selectedFace = faces[Math.floor(Math.random() * faces.length)];
53+
setClock(selectedFace);
3754
};
3855

3956
/**
@@ -78,7 +95,7 @@ exports.syncAlarms = function() {
7895
dow: item.dow,
7996
hidden: true,
8097
appid: APP_ID,
81-
js: `require('${APP_ID}.lib.js').onAlarm(${index},'${item.face}')`,
98+
js: `require('${APP_ID}.lib.js').onAlarm(${index})`,
8299
});
83100
});
84101
};

apps/schedclock/metadata.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{ "id": "schedclock",
22
"name": "Schedule Clock Faces",
33
"shortName":"Sched Clock",
4-
"version":"0.01",
4+
"version":"0.02",
55
"author": "kidneyhex",
66
"description": "Change clock faces on a schedule.",
77
"icon": "app.png",

apps/schedclock/settings.js

Lines changed: 71 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* @typedef {Object} ScheduleItemType - Individual Schedule Item
44
* @property {number} hour - Hour (0-23)
55
* @property {number} minute - Minute (0-59)
6-
* @property {string} face - Clock face source file (e.g. "myclock.js")
6+
* @property {Array<string>} faces - Clock face source files (e.g. ["myclock.js", "otherclock.js"])
77
* @property {number} dow - Bitmask for days of week [see Sched documentation]
88
*
99
* @typedef {Object} SettingsType - Overall Settings File/Object
@@ -94,7 +94,12 @@
9494

9595
// Add existing schedule items to the menu
9696
settings.sched.forEach((item, index) => {
97-
const faceName = (clockFaces.find(f => f.src === item.face) || {name: /*LANG*/"Unknown"}).name;
97+
const faces = item.faces || (item.face ? [item.face] : []);
98+
const faceName = faces.length > 1
99+
? `${faces.length} ${/*LANG*/"faces"}` // multiple faces
100+
: (faces.length === 1
101+
? (clockFaces.find(f => f.src === faces[0]) || {name: /*LANG*/"Unknown"}).name // single face
102+
: /*LANG*/"Unknown"); // no faces
98103
const dow = bitmaskToDowIndex(item.dow);
99104
const dayName = daysOfWeek[dow === undefined ? IND_EVERY_DAY : dow];
100105
const timeStr = require("locale").time(new Date(1999, 1, 1, item.hour, item.minute, 0),1)
@@ -142,6 +147,43 @@
142147
}
143148
};
144149

150+
/**
151+
* Function to edit clock face selection for a schedule item
152+
* @param {Object} currentItem The schedule item being edited
153+
* @param {Array} clockFaces Array of available clock faces
154+
* @param {Object} parentMenu The parent menu to return to
155+
*/
156+
const editClockFaceSelection = function(currentItem, clockFaces, parentMenu) {
157+
const menu = {
158+
"": { "title": /*LANG*/"Select Clock Faces" },
159+
"< Back": () => E.showMenu(parentMenu),
160+
};
161+
162+
// Add checkbox for each clock face
163+
clockFaces.forEach(face => {
164+
const isSelected = currentItem.faces.includes(face.src);
165+
menu[face.name] = {
166+
value: isSelected,
167+
onchange: v => {
168+
if (v) {
169+
// Add to selection if not already present
170+
if (!currentItem.faces.includes(face.src)) {
171+
currentItem.faces.push(face.src);
172+
}
173+
} else {
174+
// Remove from selection
175+
const idx = currentItem.faces.indexOf(face.src);
176+
if (idx !== -1) {
177+
currentItem.faces.splice(idx, 1);
178+
}
179+
}
180+
}
181+
};
182+
});
183+
184+
E.showMenu(menu);
185+
};
186+
145187
/**
146188
* Function to edit a schedule item (or add a new one if index is -1)
147189
* @param {number} index index of item to edit, or -1 to add a new item
@@ -153,10 +195,17 @@
153195
const defaultFaceSrc = clockFaces.length > 0 ? clockFaces[0].src : "";
154196

155197
const currentItem = isNew ?
156-
{ hour: 8, minute: 0, face: defaultFaceSrc, dow: BIN_EVERY_DAY } :
198+
{ hour: 8, minute: 0, faces: [defaultFaceSrc], dow: BIN_EVERY_DAY } :
157199
Object.assign({}, settings.sched[index]);
158200

159-
// Default odd items to "Every Day"
201+
// Handle backwards compatibility: convert single 'face' to 'faces' array
202+
if (currentItem.face && !currentItem.faces) {
203+
currentItem.faces = [currentItem.face];
204+
delete currentItem.face;
205+
}
206+
if (!currentItem.faces) currentItem.faces = [];
207+
208+
// Default invalid items to "Every Day"
160209
if (currentItem.dow === undefined) currentItem.dow = BIN_EVERY_DAY;
161210

162211
let dow = bitmaskToDowIndex(currentItem.dow);
@@ -179,8 +228,8 @@
179228
max: 23,
180229
format: v => {
181230
// Format as 12h time if user has that set
182-
const meridean = require("locale").meridian(new Date(1999, 1, 1, v, 0, 0),1);
183-
return (!meridean) ? v : (v%12||12) + meridean;
231+
const meridian = require("locale").meridian(new Date(1999, 1, 1, v, 0, 0));
232+
return (!meridian) ? v : (v%12||12) + meridian;
184233
},
185234
onchange: v => { currentItem.hour = v; }
186235
},
@@ -190,17 +239,11 @@
190239
max: 59,
191240
onchange: v => { currentItem.minute = v; }
192241
},
193-
/*LANG*/"Clock Face": {
194-
value: Math.max(0, clockFaces.findIndex(f => f.src === currentItem.face)),
195-
min: 0,
196-
max: clockFaces.length - 1,
197-
format: v => (clockFaces[v] && clockFaces[v].name) || /*LANG*/"None",
198-
onchange: v => {
199-
if (clockFaces[v]) currentItem.face = clockFaces[v].src;
200-
}
201-
},
242+
/*LANG*/"Clock Faces": () => editClockFaceSelection(currentItem, clockFaces, menu),
202243
/*LANG*/"Save": () => {
203-
const validationError = settings.sched.some((item, i) => {
244+
245+
// Check for schedule conflicts with duplicate times and overlapping days
246+
const hasScheduleConflict = settings.sched.some((item, i) => {
204247
if (!isNew && i === index) return false; // Don't check against self when editing
205248

206249
const timesMatch = item.hour === currentItem.hour
@@ -211,7 +254,7 @@
211254
return (item.dow & currentItem.dow) !== 0;
212255
});
213256

214-
if (validationError) {
257+
if (hasScheduleConflict) {
215258
E.showAlert(
216259
/*LANG*/"An entry for this time already exists.",
217260
/*LANG*/"Time conflict"
@@ -221,6 +264,17 @@
221264
return; // Prevent saving
222265
}
223266

267+
// Ensure at least one face is selected before saving
268+
if (!Array.isArray(currentItem.faces) || currentItem.faces.length === 0) {
269+
E.showAlert(
270+
/*LANG*/"Select at least one clock face.",
271+
/*LANG*/"No faces"
272+
).then(
273+
()=>E.showMenu(menu)
274+
);
275+
return; // Prevent saving
276+
}
277+
224278
if (isNew) {
225279
settings.sched.push(currentItem);
226280
} else {

0 commit comments

Comments
 (0)