Talking Clock
An accessible clock with a large, updating time display, scheduled speech, and soundpack chimes. Written in Python with a GTK 3 interface. Released under the WTFPL; see LICENSE.
Running
./talking-clock # open the clock window
./talking-clock -h # show help
./talking-clock -H # run headlessly with saved settings
./talking-clock -k # stop the running instance, including active audio
These are the only command-line options. Closing the clock window stops the
whole program. Headless mode runs until stopped with -k, SIGTERM, or Ctrl+C.
Only one instance runs per user. Launching normally presents the existing GUI
window, or stops a headless instance and replaces it with the GUI process.
The GUI launch first checks that a display is available; if it cannot open a
display, the headless instance keeps running. Closing the replacement window
stops the clock completely. Launching with -H while an instance is already
running leaves that instance in its current mode.
The main window has two keyboard focus targets: the time display and Settings. Focus starts on the time; Tab or Shift+Tab reaches Settings. Alt+S also opens Settings. Ctrl+Q closes the entire program from the clock or Settings window; unsaved settings are discarded. Press Space on the time display to speak the current time using your selected voice, without chimes, even if scheduled speech is disabled. The Weather tab's "Include weather in time announcement (Space)" checkbox also includes current weather when enabled; it is off by default and independent of weather in scheduled announcements. Space on the Settings button activates that button normally. Requests made while another announcement is playing are skipped to avoid overlapping audio. The display updates every second. Display preferences include 12/24 hour time, showing seconds, font size (18–240 points), and white text on black.
Settings has General, Sounds, Speech, Display, and Weather tabs. Changes take effect when saved; Cancel discards edits. General has independent checkboxes for announcements on the hour (:00), half hour (:30), and quarter hours (:15/:45). Speech and chimes can each be enabled separately. Defaults are hourly speech and chimes, 12-hour time with seconds, and a 72-point display.
The clock follows local system time. It does not announce immediately when started partway through a minute, replay missed announcements after suspend, or repeat an announcement for a local minute already announced during a backward clock change. Audio plays in a worker thread so the interface stays responsive. An announcement is skipped if the previous one is still playing.
Dependencies and installation
Required:
- Python 3.8 or later.
- For the GUI: PyGObject with GTK 3 introspection bindings.
- For the default chimes: SoX (
play) with Ogg Vorbis support. - For speech: Speech Dispatcher (
spd-say) with a configured output module. - For module/voice discovery in Settings: Speech Dispatcher's Python bindings
(the
speechdmodule).
Install your chosen audio player and speech engine separately. Yad and cron are no longer dependencies. Headless mode does not import GTK.
Run directly from this checkout using ./talking-clock, or install:
./INSTALL
Without sudo, the installer uses ~/.local/bin if it is in PATH, otherwise
~/bin if that is in PATH. If neither is present, it exits with instructions
to add one. Python modules, the default soundpack, documentation, and the desktop
entry go under $XDG_DATA_HOME, normally ~/.local/share. Soundpacks are installed
in ~/.local/share/talking-clock/sounds/ by default.
For a system-wide installation:
sudo ./INSTALL
With sudo (or as root), the launcher goes in /usr/local/bin and data goes in
/usr/share, including /usr/share/talking-clock/sounds/.
An explicit PREFIX overrides automatic selection, using $PREFIX/bin and
$PREFIX/share. DESTDIR supports package staging:
DESTDIR=/tmp/talking-clock-package PREFIX=/usr ./INSTALL
Configuration
Settings are JSON data in
$XDG_CONFIG_HOME/talking-clock/settings.json, normally
~/.config/talking-clock/settings.json. The Settings dialog creates this file
on Save. The headless clock uses the same settings and reloads external edits
while running. Invalid external edits are reported to stderr and the previous
working settings remain active.
For a headless-only installation, this minimal file enables hourly chimes without speech:
{
"hourly": true,
"half_hour": false,
"quarter_hour": false,
"chime": true,
"speak": false,
"soundpack": "default"
}
Unspecified settings use their defaults. To see every setting, save once from the GUI. Files are written atomically with permissions restricted to your user. The audio player command accepts an executable name and quoted arguments; shell operators, substitutions, and pipelines are not interpreted.
All speech uses Speech Dispatcher. The Speech tab has combo boxes for module, voice, rate, pitch, and volume. Module and voice offer System default, or you can select an installed module (such as eSpeak NG or RHVoice) and one of its voices. Voice discovery queries Speech Dispatcher without producing speech and runs in the background. Changing module reloads the available voices and resets the voice selection to System default. If discovery fails, saved module/voice selections are preserved and the dialog shows the error.
Voices are filtered to the system language and region (for example en-US).
If there is no exact match, voices for the broader language (en) are shown.
This keeps eSpeak NG's list limited to relevant voices and variants. Language
is taken from the first non-C/POSIX setting in LC_ALL, LANGUAGE,
LC_MESSAGES, and LANG, with English as the fallback. The same language is
sent to Speech Dispatcher for announcements. An unavailable saved voice is
replaced by System default when saving after successful discovery.
Rate, pitch, and volume use Speech Dispatcher's -100 to +100 scale, with 0 as normal. Combo boxes offer steps of 10 and preserve any intermediate value already saved in JSON. Some output modules may ignore unsupported controls. Choose Test speech to hear the current time with the selections currently in the Speech tab, before saving. Testing does not save changes or include chimes or weather. These preferences are used for both scheduled announcements and Space to speak the time. See the Speech Dispatcher client documentation.
Weather is optional and off by default. It uses Open-Meteo for temperature and conditions, with Fahrenheit (default) or Celsius selected in the Weather tab. For example: "77 degrees Fahrenheit and cloudy." No forecasts or other weather measurements are requested. A failed refresh silently omits weather from the spoken announcement; the clock still speaks the time, without a weather error. Technical errors go to stderr or the service journal.
When the location fields are empty, opening Settings prefills an approximate city and coordinates using IPinfo, as I38 does. IP location may be inaccurate, especially with a VPN. Review the detected location before saving. To correct it, enter a city and region (or postal code) and choose Find city, or enter latitude and longitude directly. City search uses Open-Meteo's geocoding API and GeoNames data. A changed city does not retain the old city's coordinates. Network lookups run in background threads, without freezing the interface.
IP detection contacts IPinfo using your public IP; city search sends your search text to Open-Meteo, and weather requests send coordinates to Open-Meteo. All use HTTPS. Lookups and weather require an internet connection. Automatic location detection is performed only when no location has been configured.
The client caches results under $XDG_CACHE_HOME/talking-clock/, normally
~/.cache/talking-clock/. The cache and request budget survive restarts and
are shared by simultaneous requests. Weather is fetched on demand, at most
once per quarter-hour slot (:00, :15, :30, :45), including when coordinates
change. Changing units converts the cached temperature without a new request.
Expired data is omitted if a refresh fails. The cache expires at the next
quarter hour, but that does not guarantee the underlying conditions are less
than 15 minutes old: these are weather-model estimates, and provider updates
can be delayed. See Open-Meteo's current-weather documentation.
IP location is cached for 24 hours, and a city search for 30 days. Repeated
lookups are throttled, failures back off, and HTTP Retry-After is respected.
Regular quarter-hour weather checks make at most 96 forecast requests per day.
Open-Meteo's free noncommercial service permits fewer than 10,000 calls/day,
5,000/hour, and 600/minute; its terms also list a 300,000/month limit.
See Open-Meteo's terms.
IPinfo's unauthenticated endpoint permits 1,000 daily requests shared across
the public IP; see IPinfo's public API limits.
Weather data: Open-Meteo, licensed under CC BY 4.0; the clock rounds temperatures, converts units, and describes WMO codes in plain English. City lookup data: GeoNames, via Open-Meteo geocoding.
Soundpacks
Bundled sounds live in sounds/default/ in the repository. Installed packs
are discovered under:
$XDG_DATA_HOME/talking-clock/sounds/, normally~/.local/share/talking-clock/sounds/.- Each
$XDG_DATA_DIRSentry followed by/talking-clock/sounds/, normally/usr/local/share/talking-clock/sounds/and/usr/share/talking-clock/sounds/.
Each pack has its own directory, such as sounds/default/ or sounds/bells/.
A user pack takes priority over a system pack with the same name. The checkout's
sounds/ folder is also searched when running from source. The Sounds tab can
select a discovered name or an absolute path to another pack directory.
A pack contains Ogg files:
1.oggthrough12.oggfor recordings of each hour, orbell.oggrepeated the appropriate number of times when an hour recording is absent.15.ogg,30.ogg, and45.oggfor quarter/half-hour chimes.- Optional
prepend.ogg, played before hourly chimes.
Missing sounds are reported to stderr. Speech runs before chimes, matching the original clock. Hourly bell counts always use 1–12, including in 24-hour mode.
Migrating from the Bash version
The old Bash executable and separate Yad GUI have been replaced. There is no built-in crontab management and no one-shot announcement mode. Remove old recurring talking-clock cron entries before switching to the persistent clock; old entries now invoke a GUI rather than a one-shot announcement. The installer and clock do not modify your crontab.
If no JSON settings exist, the clock reads known assignments from
$XDG_CONFIG_HOME/talking-clock/talking-clockrc, or /etc/talking-clockrc as a
fallback. It imports speech on/off, chime, format, audio command, soundpack,
and weather preferences as data without executing the file. Old engine-specific
voice commands and earlier Python engine settings are discarded in favor of
Speech Dispatcher defaults; choose module/voice and tuning in the Speech tab.
Shell expressions in old settings are not evaluated. Saving in the GUI writes
the new JSON file and
leaves the old file untouched. Old CLI options are removed; set those options
in Settings instead. The old cron schedule is not imported; choose the desired
interval checkboxes in General. Existing external soundpack directories can
still be used by their absolute paths.
Headless startup with systemd
For a user service, create ~/.config/systemd/user/talking-clock.service:
[Unit]
Description=Talking Clock
[Service]
ExecStart=/usr/local/bin/talking-clock -H
Restart=on-failure
[Install]
WantedBy=default.target
Then enable it with systemctl --user enable --now talking-clock.service.
Use your installed executable path if a different prefix was chosen. Run it
under your own user so it can access your audio session. Configure the clock
before starting the service. A normal talking-clock launch replaces the
headless instance with the GUI. The example's Restart=on-failure leaves an
intentional stop stopped; using Restart=always would restart headless mode
after takeover or -k. View headless errors with
journalctl --user -u talking-clock.service.
Development checks
python3 -m unittest discover -s tests -v
shellcheck INSTALL
For opt-in GTK checks on a working desktop:
TALKING_CLOCK_GUI_TESTS=1 python3 -m unittest discover -s tests -v
Tests disable or mock clock speech and sound playback. Desktop checks cover widget focus, live display updates, settings, and window shutdown; actual screen-reader speech and visual usability still need human testing.