Atom Time
About
Let’s create a basic mini-app to set a time on the SB Watch with a numeric keyboard. The functionality will be similar you can achieve with the TimeSmith app, however, you can directly connect Atom and SB Watch and you will need no phone. This mini-app is called Atom Time.
Technically you only need Atom and an SB Watch, but this app supports PeekSmith 3 as well to display the time entered and to give you visual feedback about the time you set. If you have no PeekSmith connected, then it will be ignored.
We are sharing a basic version, and the source code of the built-in Atom Time mini-app.
Basic Version
Let’s start with a basic version. The advantage of this code is that it is shorter, and maybe easier to understand.
After uploading this code to Atom, the main()
the function will be called and connect to your PeekSmith and SB Watch. As the device names are not specified, it will use the device names specified in the Settings (*
= any device by default). It will display “Atom Time” (\n
means a new line, so in two lines) on the PeekSmith as soon as it is connected.
There are three variables declared in the lines 8 – 15. The time
variable will contain the time you enter. The timeout
variable will contain a timeout variable, which is about sending the time entered after 4 seconds of the last key press. The mapping
contains the key mapping, what the keys mean.
The send()
function from line 18 is about sending the time to the watch. It is called when you press the bottom left button, or after 4 seconds. The 4 seconds timer (timeout
) is cleared to prevent calling the send function again. If the time
variable is an empty string, there’s nothing to send, so the code returns. If 4444
or 9999
was entered, it sets the current time, otherwise the time has been entered. Finally the time
variable will be cleared.
The onButtonClick
function is called with the button ID (between 0 and 11, from top left to bottom right) pressed, and the first line maps it to a string that can be easier to understand than the button ID. If it is s
, then it immediately sends the time. If it is <
, then it deletes the last number of the time, otherwise it adds the number to the end of the time
string. Then it clears the timeout
timer, and set it to 4 seconds. Finally, it displays the actual time entered on the PeekSmith’s screen.
The function printFormattedTime
is about displaying the time going to be set on the watch. The second parameter is if the hands are moving, if it is true, then two stars will be displayed before and after the time.
And the onEvent
function is responsible for event handling. It cares about the hands started, hands finished and button click events, and calls the printFormattedTime
and onButtonClick
functions.
function main() {
ps.connect();
ps.print('Atom\nTime');
sbwatch.connect();
// sbwatch.setTime('12:00');
}
let time = ''; // the time entered
let timeout = 0; // will contain a timeout ID
let mapping = [ // button layour
'1', '2', '3',
'4', '5', '6',
'7', '8', '9',
's', '0', '<'
];
function send() {
clearTimeout(timeout); // don't trigger send
if (time === '') return; // nothing to set
if (time === '4444' || time === '9999') {
sbwatch.setCurrentTime();
} else {
sbwatch.setTime(time);
}
time = ''; // reset to empty string
}
function onButtonClick(buttonId) {
let key = mapping[buttonId];
// send immediately
if (key === 's') {
send();
return;
}
// delete the last number or add a new
if (key === '<') {
time = strSub(time, 0, -1);
} else {
time += key;
}
// delete the previous timer, and start a new with 4s
clearTimeout(timeout);
timeout = setTimeout(send, 4000);
// display the time entered
ps.print(`<${time}>`);
}
// displays the processed time, and hand movement indicator
function printFormattedTime(time, moving) {
if (moving) time = `*${time}*`;
ps.print(time);
}
// there are events coming from the SB Watch and the buttons of Atom
function onEvent(e) {
console.log(e.value, e.type, e.source);
if (e.source === 'sbwatch:hands' && e.type === 'started') {
atom.vibrate('\'');
printFormattedTime(e.value, true);
}
if (e.source === 'sbwatch:hands' && e.type === 'finished') {
atom.vibrate('\'');
printFormattedTime(e.value, false);
}
if (e.type === 'click' && e.source === 'atom:button') {
onButtonClick(parseInt(e.value));
}
}
Advanced Version (The Built-in Atom Time mini-app)
Here’s the Atom Time code of the built-in mini-app. It supports several configuration settings (the timeout is not fix 4 seconds, if the time should be sent after entering the 4th number, if Pi Revelations information to be displayed), and a little bit “smarter”.
let time = ''; // the time entered
let lastTime = '';
let sent = false;
let timeout; // will contain a timeout
function main() {
ps.connect();
sbwatch.connect();
}
// Atom Time layout
const layout = [
'1', '2', '3',
'4', '5', '6',
'7', '8', '9',
's', '0', '<'
];
function printTimeEntered() {
let rest = '';
for (let i = strLen(time); i < 4; i++) {
rest += '_';
}
let text = `${time}${rest}\n`;
ps.print(text);
}
// displays the processed time, and hand movement indicator
function printFormattedTime(formatted, moving) {
if (moving) formatted = `*${formatted}*`;
const displayPi = config.get('atomtime.pi.display');
if (time === '' || !displayPi) {
ps.print(formatted);
return;
}
const piInfo = db.query("pi", parseInt(time));
for (let i = strLen(time); i < 4; i++) time = '0' + time;
ps.print(`${formatted}\n${time}\n${piInfo.page} ${piInfo.line} ${piInfo.across}`);
}
function send() {
clearTimeout(timeout); // don't trigger send
if (time === '') return; // nothing to set
if (time === '4444' || time === '9999') {
sbwatch.setCurrentTime();
time = '';
} else {
sbwatch.setTime(time);
}
sent = true; // will reset time to empty string
}
function onButtonClick(buttonId) {
if (sent) time = ''; // reset to empty string if already sent
sent = false;
const processAfterFourDigits = config.get('atomtime.submit.after_four_digits');
const key = layout[buttonId];
switch (key) {
case 's':
send();
lastTime = '';
return;
case '<':
if (time === '' && lastTime !== '') time = lastTime;
time = strSub(time, 0, -1);
break;
default:
if (strLen(time) < 4) {
time += key;
}
if (strLen(time) >= 4 && processAfterFourDigits) {
lastTime = time;
send();
return;
}
break;
}
// delete the previous timer, and start a new
clearTimeout(timeout);
const processAfter = config.get('keyboard.submit.after');
if (processAfter > 0) timeout = setTimeout(() => {
send();
lastTime = time;
}, processAfter);
// display the time entered
printTimeEntered();
}
function onButtonLongPress(buttonId) {
sbwatch.setCurrentTime();
time = '';
}
// there are events coming from the SB Watch and the buttons of Atom
function onEvent(e) {
if (e.source === 'ps:ble' && e.type === 'connected') {
ps.print('Atom\nTime');
setTimeout(printTimeEntered, 2000);
return;
}
if (e.source === 'sbwatch:hands') {
atom.vibrate('\'');
if (e.type === 'started') {
printFormattedTime(e.value, true);
}
if (e.type === 'finished') {
printFormattedTime(e.value, false);
}
}
if (e.source === 'atom:button') {
const buttonId = parseInt(e.value);
if (e.type === 'click') {
onButtonClick(buttonId);
}
if (strSub(e.type, 0, 5) === 'click' && strLen(e.type) === 6) {
const clickCount = parseInt(strCharAt(e.type, 5));
for (let i = 0; i < clickCount; i++) {
onButtonClick(buttonId);
}
}
if (e.type === 'longpress') {
onButtonLongPress(buttonId);
}
}
}