Skip to content

Commit 40aeb48

Browse files
authored
Merge pull request #4277 from peterbuehler/master
Pure Numerals Clock Version 0.04
2 parents af22bbf + b1a0835 commit 40aeb48

7 files changed

Lines changed: 190 additions & 51 deletions

File tree

apps/purenumerals/ChangeLog

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
0.03: first released version.
1+
0.03: First released version.
2+
0.04: Battery level with ringbuffer, blinking red dot if device on.

apps/purenumerals/README.md

Lines changed: 44 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,28 @@
11
# Pure Numerals Clock
22

3-
## System settings
3+
> **Time, reduced to digits.**
44
5-
This chapter describes some system settings that affect the behavior of the Pure Numerals Clock.
5+
Pure Numerals is a digital watch face that strips away all unnecessary elements and translates time into two bold blocks of numbers. Hours at the top, minutes at the bottom — in vivid contrasting colors against a deep black background. Radically simple, extremely easy to read, and looking like a digital neon sign on your wrist.
6+
7+
## Features
8+
9+
- **Oversized, softly rounded numerals** for maximum readability at a glance
10+
- **Strong contrasting colors** on a deep black background
11+
- **Radically focused** on the time — no distractions, no clutter
12+
- **Optimized for Bangle.js** — smooth, responsive, and power-efficient
13+
- **For users who love typographic presence** and the beauty of pure digital numerals
14+
15+
---
16+
17+
## System Settings
618

719
### LCD Timeout
820

9-
The _LCD Timeout_ can be found under _Settings → System → LCD_ and determines the duration after which the watch automatically goes into "sleep" mode without user interaction. During this time, the touchscreen and backlight are switched off to prevent unwanted user interactions and reduce power consumption. The _LCD Timeout_ is set to 10 seconds by default.
21+
Found under _Settings → System → LCD_. Determines how long the watch waits before going to sleep — switching off the touchscreen and backlight to save power. Default: 10 seconds.
1022

11-
### Waking up the clock
23+
### Waking Up the Clock
1224

13-
Under _Settings → System → LCD,_ you can configure one or more options to determine the user interaction used to wake the clock from sleep mode. The following options are available:
25+
Under _Settings → System → LCD_, configure which interactions wake the clock from sleep:
1426

1527
- Wake on Button
1628
- Wake on Tap
@@ -20,54 +32,57 @@ Under _Settings → System → LCD,_ you can configure one or more options to de
2032

2133
### LCD Brightness
2234

23-
The _LCD Brightness_ setting adjusts the brightness of the display's backlight. This setting can be found under _Settings → System → LCD_ . The brightness is set to a value between 0.0 and 1.0 and affects power consumption and therefore the watch's battery life. In practice, a value of 0.3 has proven effective.
35+
Found under _Settings → System → LCD_. Accepts a value between 0.0 and 1.0. A value of 0.3 is a good practical starting point.
36+
37+
### Summer and Winter Time
2438

25-
### Summer and winter time
39+
The Bangle only detects time changes when synced with a smartphone. If the time is off, correct it manually by adjusting the time zone under _Settings → System → Adjust Local_.
2640

27-
The Bangle only recognizes the time change if it is connected to a smartphone and synchronizes with it. Therefore, the time may need to be corrected manually. You can do this by changing the _time zone_ under _Settings →_ Adjust _System → Local accordingly._
41+
### Default Clock
2842

29-
### Setting the Default Clock
43+
Multiple watch faces can be installed on the Bangle. Set which one launches by default under _Settings → System → Clock_.
3044

31-
Multiple watch faces can be installed on the Bangle. Under _Settings →_ _System → Clock_ allows you to set which clock should be used by default. This clock will be displayed when you exit the app launcher.
45+
---
3246

3347
## Primary Views
3448

35-
The primary views are used to display the date and time, as well as the step counter and pulse measurement. A carousel navigation was implemented for switching views. Swiping from right to left displays the next view (corresponding to scrolling forward). Swiping from left to right displays the previous view. The order of the subchapters corresponds to the order of the carousel navigation.
49+
Primary views show the date, time, step count, and pulse. Switch between them using carousel navigation: swipe left for the next view, swipe right for the previous.
3650

3751
### Day/Month
38-
This view displays the date. The day is shown in the upper half, the month in the lower half. This order corresponds to the European date format.
52+
Displays the date in European format — day in the upper half, month in the lower half.
3953

4054
### Hours/Minutes
41-
This view displays the hours and minutes. This view is displayed when the clock starts or when the LCD timeout has expired and the clock is put to sleep.
55+
Displays hours and minutes. This is the default view shown on startup and after waking from sleep.
4256

4357
### Minutes/Seconds
44-
This view displays the minutes and seconds. The same color is used for the minutes as in the Hours/Minutes view.
58+
Displays minutes and seconds, using the same color for minutes as the Hours/Minutes view.
4559

4660
### Pedometer
47-
This view displays the number of steps recorded on a given day. The counter resets to 0 when the date changes. The font size of the step count scales automatically.
61+
Displays the step count for the current day. Resets at midnight. Font size scales automatically.
62+
63+
### Pulse Measurement
64+
Activates the heart rate sensor when opened and displays the measured pulse once available. Font size scales automatically.
4865

49-
### Pulse measurement
50-
This view displays the measured pulse. The pulse measurement sensor is activated when the view is opened. As soon as measurements are available, the measured pulse is displayed. The font size is automatically scaled.
66+
The first reading takes approximately 20 seconds. To allow for this, the LCD timeout is disabled in this view and re-enabled when you leave it.
5167

52-
Since it takes approximately 20 seconds for the first measurement to be displayed, the LCD timeout is deactivated in this view. Therefore, the watch does not go to sleep during heart rate measurement. The LCD timeout is reactivated when the view is exited.
68+
---
5369

5470
## Secondary Views
5571

56-
Secondary views are accessed from primary views through appropriate user interaction. When exiting a secondary view, the last active primary view is automatically displayed.
72+
Secondary views are accessed by tapping specific areas of the display. Exiting returns you to the last active primary view.
5773

58-
### Battery indicator
74+
### Battery Indicator
5975

60-
The battery indicator is accessed by tapping the upper right corner of the display.
61-
The charging indicator bar is colored as follows:
76+
Access by tapping the **upper right corner**. The charge bar uses the following color coding:
6277

63-
- Green: Charge level > 40%
64-
- Orange: Charge level between 20% and 40%
65-
- Red: Charge level < 20%
78+
- **Green**: > 40%
79+
- **Orange**: 20–40%
80+
- **Red**: < 20%
6681

67-
The battery indicator will automatically exit after a timeout of 3 seconds.
82+
Closes automatically after 3 seconds.
6883

69-
### flashlight
84+
### Flashlight
7085

71-
The flashlight icon displays a white rectangle at full brightness. The flashlight is activated by double-tapping the lower left corner of the display. The double-tapping feature prevents accidental activation of the flashlight.
86+
Access by **double-tapping the lower left corner**. Displays a full-brightness white screen.
7287

73-
The LCD timeout is disabled while the flashlight is on. Tapping the display exits the view. This re-establishes the LCD timeout and restores the original brightness.
88+
The LCD timeout is disabled while active. Tap anywhere to exit, which restores the previous brightness and re-enables the timeout.

apps/purenumerals/app-icon.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/purenumerals/app.js

Lines changed: 125 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
// Flashlight (double touch bottom left) and Battery (touch top right)
99
//
1010
// 27.11.2025 Peter Bühler
11-
//
11+
// 29.06.2026 Battery with Ringbuffer
12+
// Watchdog (blinking red dot when device is on)
1213

1314
{
1415
// globals
@@ -404,9 +405,11 @@
404405

405406
function draw() {
406407
clearBackground();
407-
let percent = E.getBattery(); // Emulator : 0
408+
// let percent = E.getBattery(); // Emulator : 0
408409
//percent = 19;
409410

411+
let percent = app.getBattery();
412+
410413
// draw battery
411414
let bx = CENTER - 36, by = CENTER - 42;
412415
let bw = 72, bh = 30;
@@ -556,6 +559,108 @@
556559

557560
};
558561

562+
// factory function for the battery manager
563+
let createBatteryManager = function() {
564+
565+
// calculates average of battery charge
566+
567+
let values = [ -1, -1, -1, -1, -1 ]; // Ringbuffer
568+
let index = 0;
569+
let interval;
570+
const timeout = 1000 * 60 * 5; // 5 Min
571+
572+
function putMeasure() {
573+
let value = E.getBattery();
574+
let ind = index % values.length;
575+
values[ind] = value;
576+
index++;
577+
}
578+
579+
// returns the battery charge
580+
function getValue() {
581+
let sum = 0;
582+
let count = 0;
583+
for (const v of values) { // calculate average
584+
if ( v !== -1) {
585+
sum += v;
586+
count++;
587+
}
588+
}
589+
return sum / count;
590+
}
591+
592+
function start() {
593+
if (interval) { clearInterval(interval); }
594+
putMeasure(); // first measure
595+
interval = setInterval(function() {
596+
putMeasure();
597+
}, timeout);
598+
}
599+
600+
function stop() {
601+
if (interval) { clearInterval(interval); }
602+
}
603+
604+
return { start, stop, getValue } ;
605+
606+
};
607+
608+
// factory function for watchdog ---------------------------------------
609+
// draws red blinking dot when not watch is locked
610+
611+
let createWatchdog = function() {
612+
613+
const radius = 4;
614+
const gap = 4;
615+
const x = DISPLAY_SIZE - radius - gap;
616+
const y = DISPLAY_SIZE - radius - gap;
617+
618+
let on = false;
619+
let locked = false;
620+
let visible = true;
621+
let interval;
622+
623+
function init() {
624+
interval = setInterval(updateState, 500);
625+
}
626+
627+
function updateState() {
628+
locked = Bangle.isLocked();
629+
on = !on;
630+
if (visible) {
631+
draw();
632+
}
633+
}
634+
635+
function shutdown() {
636+
clearInterval(interval);
637+
}
638+
639+
function draw() {
640+
if (locked) {
641+
drawDot('#000000'); // use black (gray is blinking)
642+
}
643+
else if (on) {
644+
drawDot('#FF0000'); // red
645+
}
646+
else {
647+
drawDot('#000000'); // black
648+
}
649+
}
650+
651+
function drawDot(color) {
652+
g.setColor(color);
653+
g.fillCircle(x, y, radius);
654+
}
655+
656+
function setVisible(vis) {
657+
visible = vis;
658+
}
659+
660+
return { init, shutdown, setVisible };
661+
662+
};
663+
559664
// factory function for the app
560665
let createApp = function () {
561666

@@ -582,6 +687,9 @@
582687
let lastTouchTime = 0;
583688
let lastTouchArea = undefined;
584689

690+
let batteryManager;
691+
let watchdog = createWatchdog();
692+
585693
function onSwipe(lr, td) {
586694
// console.log("onSwipe() lr: " + lr + " td: " + td);
587695
if (currentKey > MAX_CAROUSEL_KEY) { return; } // only when carousel view
@@ -680,6 +788,7 @@
680788
currentView = viewMap[currentKey];
681789
lastCarouselKey = (currentKey <= MAX_CAROUSEL_KEY) ? currentKey : lastCarouselKey;
682790
currentView.enter();
791+
watchdog.setVisible(key <= MAX_CAROUSEL_KEY);
683792
}
684793

685794
// returns offset for step view
@@ -711,6 +820,11 @@
711820
require("Storage").writeJSON(FILE_NAME, data);
712821
}
713822

823+
// returns the battery charge
824+
function getBattery() {
825+
return batteryManager.getValue();
826+
}
827+
714828
// start app
715829
function start() {
716830
// console.log("app start()");
@@ -723,6 +837,11 @@
723837
Bangle.on("midnight", onMidnight);
724838
Bangle.on("lock", onLock);
725839

840+
// start battery manager
841+
batteryManager = createBatteryManager();
842+
batteryManager.start();
843+
844+
watchdog.init(); // start watchdog
726845
showView(1); // show hour/minute
727846
}
728847

@@ -731,16 +850,19 @@
731850
// console.log("app shutdown()");
732851
if (currentView) { currentView.leave(); }
733852

853+
batteryManager.stop();
854+
734855
// unregister event handler
735856
Bangle.removeListener("swipe", onSwipe);
736857
Bangle.removeListener("touch", onTouch);
737858
Bangle.removeListener("midnight", onMidnight);
738859
Bangle.removeListener("lock", onLock);
739860

861+
watchdog.shutdown();
740862
writeStepData();
741863
}
742864

743-
return { start, shutdown, showLastCarouselView, showView, getStepOffset };
865+
return { start, shutdown, showLastCarouselView, showView, getStepOffset, getBattery };
744866

745867
};
746868

apps/purenumerals/app.png

127 Bytes
Loading

apps/purenumerals/metadata.json

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
1-
{ "id": "purenumerals",
2-
"name": "Pure Numerals Clock",
3-
"shortName":"Pure Numerals",
4-
"icon": "app.png",
5-
"version":"0.03",
6-
"author":"OST Eastern Switzerland University of Applied Sciences",
7-
"description": "A watch with a step counter and heart rate measurement. You can switch views by swiping left or right. Additionally, a flashlight function (double-tap the bottom left) and a battery indicator (tap the top right) are available.",
8-
"tags": "clock",
9-
"type": "clock",
10-
"supports": ["BANGLEJS2"],
11-
"allow_emulator": true,
12-
"readme":"README.md",
13-
"storage": [
14-
{"name":"purenumerals.app.js","url":"app.js"},
15-
{"name":"purenumerals.img","url":"app-icon.js","evaluate":true}
16-
]
17-
}
1+
{ "id": "purenumerals",
2+
"name": "Pure Numerals Clock",
3+
"shortName":"Pure Numerals",
4+
"icon": "app.png",
5+
"version":"0.04",
6+
"author":"OST Eastern Switzerland University of Applied Sciences",
7+
"description": "Pure Numerals is a minimalist digital watch face that displays easy-to-read time and date as two bold blocks of numbers in vivid colors on a deep black background. It also includes a step counter, heart rate measurement, a flashlight and a battery indicator.",
8+
"tags": "clock",
9+
"type": "clock",
10+
"supports": ["BANGLEJS2"],
11+
"allow_emulator": true,
12+
"readme":"README.md",
13+
"storage": [
14+
{"name":"purenumerals.app.js","url":"app.js"},
15+
{"name":"purenumerals.img","url":"app-icon.js","evaluate":true}
16+
],
17+
"screenshots": [{"url":"screenshot.png"}]
18+
}

apps/purenumerals/screenshot.png

746 Bytes
Loading

0 commit comments

Comments
 (0)