Zeitansage

Weil jemand kürzlich ganz erstaunt darüber war, dass meine Rechner jede Viertelstunde automatisch die Zeit ansagen, verrate ich zu gern, wie ich das gemacht habe.

Zunächst einmal zur Motivation. Es ist für mich relativ leicht, am Computer die Uhrzeit zu vergessen. Ja, ich lasse mir die Zeit mit einem Widget in einem Xfce-Panel anzeigen, aber wenn ich so konzentriert auf eine Sache bin, dass ich darüber die Zeit vergessen könnte, schaue ich dort natürlich nicht hin. Deshalb habe ich gern ein kleines Signal, das ich wahrnehmen kann, ohne dass es meine Aufmerksamkeit mit allzuviel Gewalt an sich reißt.

Natürlich ist der hier skizzierte Weg so nur unter Linux gangbar.

Für mich war es sehr naheliegend, einfach in regelmäßigen Abständen die Uhrzeit sprechen zu lassen. Für die Sprachsynthese verwende ich espeak, weil das auf den Debian- und Ubuntu-Installationen, auf denen ich arbeite, in aller Regel schon installiert ist. So kann ich trotz wechselnder Arbeitsrechner meine Skripte ohne Änderung nahezu überall benutzen, ohne dass ich etwas nachinstallieren müsste. Wer lieber ein anderes Programm verwendet – espeak liefert nicht gerade eine hübsche Sprachausgabe – wird nach Lesen der entsprechenden man-page für sein Lieblingsprogramm schon herausbekommen, wie sich mein kleines Hackwerk anpassen lässt.

Die von mir hingehackten Skripten sind recht »quick and dirty«, es sind also keine Meisterwerke der Softwareentwicklung. Aber sie funktionieren für den vorgesehenen Zweck gut.

Eine ganz einfache Lösung

In meiner ersten Version hat das Skript einfach gesagt: »Es ist jetzt 13 Uhr 22″. Wer eine solche Ansage gut genug findet, kann das mit sehr wenig Mühe erreichen:

#!/bin/sh

date +’$H $M’ | 
awk ’{ printf "Es ist jetzt %d Uhr %d", $1, $2 }’ |
espeak -v de 2>/dev/null

Dieses bemerkenswert primitive Shellskript spricht bei Aufruf die Uhrzeit in der Form »Es ist jetzt zehn Uhr sechsundfünfzig«. Für Menschen, die mit Digitaluhren großgeworden sind und die Uhrzeit niemals anders bezeichnen, ist das völlig hinreichend – vor allem, wenn sie sich nicht an Fehlerchen wie »Es ist jetzt eins Uhr null« stören. Es könnte etwa in die crontab eingetragen werden, um alle fünfzehn Minuten die Zeit anzusagen – wobei vielleicht Vorsorge getragen werden sollte, damit es das nur tut, wenn man selbst angemeldet ist. (Siehe hierzu weiter unten).

Das stellt mich aber nicht zufrieden…

Ich bin nun schon ein etwas älterer Mensch, mit Analoguhren großgeworden und bevorzuge deshalb die althergebrachte, ausführliche Bezeichnung der Uhrzeit. Anstelle von »Es ist jetzt zehn Uhr sechsundfünfzig« würde ich lieber »Es ist jetzt vier Minuten vor elf« hören, weil ich das sofort und ohne weiteres Nachdenken auffassen kann. Dies natürlich mit den üblichen Besonderheiten zu den Viertelstunden, den halben Stunden und zu den vollen Stunden und allen damit verbundenen sprachlichen Unregelmäßigkeiten.

Tja, und das alles erfordert doch ein bisschen mehr Nachdenken als das zuvor kurz angerissene, einfache und zudem leicht fehlerhafte Shellskript.

Weil ich sehr genau weiß, dass sich niemand solche Gedanken gern macht, gibt es meine Skripten hier zum freien Download – wer unbedingt eine Lizenz dafür zu brauchen glaubt, fühle sich unter den Bedingungen der Piratenlizenz lizenziert… 😉

Ausgabe der Uhrzeit als deutscher Text

Ich bin das Problem in zwei Schritten angegangen.

Als erstes habe ich ein kleines Programm geschrieben, dass die Uhrzeit in deutscher Sprache als Text ausgibt. Dabei gebe ich auch Zahlwörter als Text aus, damit immer die richtig deklinierten Zahlwörter verwendet werden.

So ein Programm kann man natürlich auch – wie die sehr einfache Lösung oben – in awk schreiben, wenn man zur Selbstquälerei neigt. Angesichts der vielen Ausnahmen habe ich mich aber für das syntaktisch deutlich verständlichere Python entschieden; ich wollte schließlich für so eine Kleinigkeit nicht übermäßig viel Zeit mit Fehlersuche verbringen.

Dieses Python-Programm sieht nur auf dem ersten Blick etwas abstoßend aus, weil es für seinen sehr einfachen Zweck doch sehr lang geraten ist. Diese Länge spiegelt die Unregelmäßigkeiten und die Unlogik gesprochener Sprache wider:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import datetime

einer = [
    ’null’,
    ’eins’, ’zwei’, ’drei’, ’vier’, ’fünf’,
    ’sechs’, ’sieben’, ’acht’, ’neun’, ’zehn’,
    ’elf’, ’zwölf’, ’dreizehn’, ’vierzehn’, ’fünfzehn’,
    ’sechzehn’, ’siebzehn’, ’achtzehn’, ’neunzehn’
]

zehner = [
    ’null’,
    ’zehn’, ’zwanzig’, ’dreißig’,
    ’vierzig’, ’fünfzig’, ’sechzig’
]

def i2de(n):
    assert n >= 0
    if n < 20:
        return einer[n]
    else:
        z = n / 10
        e = n % 10
        if e == 0:
            return zehner[z]
        else:
            if e == 0:
                return zehner[z]
            elif e == 1:
                return ’einund%s’ % (zehner[z],)
            else:
                return ’%sund%s’ % (einer[e], zehner[z])
        
def chour(h):
    if h >= 12:
        h -= 12
    if h == 0:
        h = 12
    return i2de(h)

def datetime2text(dt):
    st=dt.hour
    mi=dt.minute
    if mi % 15 == 0:
        if mi == 15:
            return ’Viertel nach %s’ % (chour(st),)
        elif mi == 30:
            return ’Halb %s’ % (chour(st+1),)
        elif mi == 45:
            return ’Viertel vor %s’ % (chour(st+1),)
        elif mi == 0:
            if st == 1:
                return ’ein Uhr’
            else:
                return ’%s Uhr’ % (chour(st),)
    else:
        if mi == 1:
            return ’eine Minute nach %s’ % (chour(st),)
        elif mi == 59:
            return ’eine Minute vor %s’ % (chour(st + 1),)
        elif mi < 30:
            return ’%s Minuten nach %s’ % (i2de(mi), chour(st))
        else:
            return ’%s Minuten vor %s’ % (i2de(60 - mi), chour(st + 1))

if __name__ == ’__main__’:
    print(datetime2text(datetime.datetime.now()))

Wer es verstehen möchte: Es ist viel einfacher, als man denkt.

Die Funktion i2de ist eine allgemeine Funktion, die aus einem int ein Zahlwort macht – hier natürlich mit sehr beschränktem Gültigkeitsbereich. Die Arrays mit den Zahlwörtern bieten sich natürlich an (und sie sind hier etwas weiter befüllt, als dies nötig ist). Die Funktion chour löst das Problemchen, dass die Stunde zwar modulo 12 ausgegeben werden soll, dass aber andererseits null Uhr zu zwölf Uhr wird. Die eigentliche Arbeit wird in datetime2text getan; hier gibt es eine Menge Sondercode für die vollen Viertelstunden, für die Zahl eins mit ihren Deklinationen und schließlich wird noch zwischen nach der letzten Stunde oder vor der nächsten Stunde unterschieden. Das ist übrigens der Grund, weshalb ich nach sehr kurzem Nachdenken awk verworfen habe… 😉

(Und ja, natürlich kann man das auch gut in Perl machen. Nur awk würde ich in diesem Fall nicht empfehlen. Außer bei vorhandenem Hang zur Selbstquälerei.)

Wenn dieses Skript – ich habe es unter dem naheliegenden Dateinamen uhrzeit gespeichert – aufgerufen wird, gibt es die aktuelle Uhrzeit als Text aus. Nur fehlt nur noch, dass dieser Text vorgelesen wird.

Vorlesen der Uhrzeit

Das Skript zum Vorlesen der Uhrzeit ist nun allerdings trivial:

#!/bin/sh

PATH=/bin:/usr/bin
if who -u | grep -q ’^elias’
then
    echo ’Es ist jetzt’ `/home/elias/bin/uhrzeit` |
    espeak -v de -s 120 2>/dev/null &
fi

Natürlich muss der Pfad zum Skript uhrzeit angepasst werden, und natürlich muss der grep auf den eigenen Benutzernamen gehen. Beide Anpassungen sind einfach.

Dieser grep zusammen mit dem who -u sorgt übrigens dafür, dass es nur dann zur Sprachausgabe kommt, wenn ich auch eingeloggt bin. Sicher, wenn ich das Skript von Hand aufrufe (oder durch einen Klick starte), ist das immer der Fall, aber es steht im Zusammenhang mit dem abschließenden Schritt.

Und jetzt jede Viertelstunde

Denn ich wollte ja, dass mir jede Viertelstunde automatisch die Zeit angesagt wird, wenn ich angemeldet bin.

Dazu habe ich das Shellskript zum Vorlesen der Uhrzeit – das ich unter dem ebenfalls naheliegenden Namen sagzeit gespeichert habe – in meine crontab eingetragen.

Einmal crontab -e getippt und mit meinem Lieblingseditor die folgende Zeile eingefügt:

*/15 * * * * /home/elias/bin/sagzeit

Nach dem Speichern und dem Beenden des Editors wird mein Skript sagzeit zu jeder vollen Viertelstunde ausgeführt. Da dies auch geschieht, wenn der Rechner hochgefahren ist, ich aber nicht angemeldet bin, ist jetzt klar, was der Zweck der zusätzlichen Bedingung im Shellskript ist: Die Kiste soll nicht einfach unerwünscht die Uhrzeit brabbeln, sondern es nur für mich tun.

Näheres zum Format der Datei lässt sich mit man 5 crontab in Erfahrung bringen, und wer noch nie davon gehört hat, sollte das ruhig einmal tun. Es ist ausgesprochen nützlich, Dinge automatisieren zu können. Schließlich ist der Computer dafür da, dem Menschen zu dienen und nicht umgekehrt.

Tja, und das ist Grund, warum ich so gern unixoide Betriebssysteme vor mir habe… 😉

Dieser Beitrag wurde unter Download abgelegt und mit , , , verschlagwortet. Setze ein Lesezeichen auf den Permalink.

3 Antworten zu Zeitansage

  1. Falk Friedrich sagt:

    Vielen Dank für den Artikel. Dadurch hab ich espeak kennengelernt, was ich vorher nicht kannte.

    Ich habe mir ein paar Verbesserungsideen ausgedacht.
    Soweit ich sehe benutzt du awk in erster Linie um führende 0en von der Uhrzeit zu entfernen. gnu date hat dafür eine eigene passende funktionalität, so dass man awk komplett einsparen kann.


    #!/bin/sh
    date +'Es ist jetzt %-H Uhr %-M' |
    espeak -v de 2>/dev/null

    Was mich noch stört ist, dass auch bei vollen Stunden die Minuten (Es ist jetzt neun Uhr null) mit ausgesprochen werden.
    Daher:

    #!/bin/sh

    h=`date +"%-H"`
    m=`date +"%-M"`
    if test $h -eq 0; then
    espeak -v de -s 120 "Es ist $h Uhr" 2>/dev/null
    else
    espeak -v de -s 120 "Es ist $h Uhr $m" 2>/dev/null
    fi

    • Die Hauptaufgabe meines Python-Programmes ist es, eine normale sprachliche Ausgabe zu machen, so etwas wie »Es ist jetzt drei Minuten vor drei«. Meine erste Version war auch ein Shellskript, das dann aber ein bisschen zu kompliziert wurde (und mehrfach awk bemühte).

      Für die Angabe einer militärischen Zeit »Es ist jetzt achtzehn Uhr vierundvierzig« ist ein Shellskript hinreichend und kürzer, in der Tat. Dann kann die 1 auch ruhig als »eins« ausgesprochen werden, nur für die Stunde muss man sich etwas überlegen, damit er nicht »Es ist jetzt eins Uhr« sagt. 😉

  2. Jakob Flierl sagt:

    Hi Elias, könntest Du eventuell noch das »Es ist jetzt eins Uhr« in Deinem Python-Skript fixen? – Thx!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert