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 speechd module).

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_DIRS entry 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.ogg through 12.ogg for recordings of each hour, or bell.ogg repeated the appropriate number of times when an hour recording is absent.
  • 15.ogg, 30.ogg, and 45.ogg for 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.

S
Description
A highly configurable talking clock written in bash.
Readme
751 KiB
0 Stars 1 Watchers 0 Forks
Languages
Python 100%