Ereignisgesteuerte Automatisierung unter macOS mit Crank

Crank Keyvisual. Dekorativ

Crank ist ein Programm, das im Hintergrund läuft und ver­schie­de­ne Aktionen aus­füh­ren kann, wenn im System irgend­was pas­siert. So kann Crank dar­auf reagie­ren, wenn ein Kopfhörer über Bluetooth ver­bun­den wird oder ein Screenshot in einem Ordner lan­det. Cranks Aufgabe ist es, auf bestimm­te Ereignisse zu lau­schen, die als Trigger defi­niert sind, und dar­auf­hin eine Aktion anzu­sto­ßen. Dabei funk­tio­niert Crank ähn­lich wie Shortery, das aller­dings nur als Aktion die Ausführung von Apple Kurzbefehlen unter­stützt. Auch in Crank kön­nen Kurzbefehle als Aktion genutzt wer­den, dar­über hin­aus Skripte, die in unter­schied­li­chen Sprachen geschrie­ben sind.

Da ich mich für die Automatisierung unter macOS und das Erstellen von Skripten inter­es­sie­re, fand ich den Ansatz von Crank inter­es­sant und woll­te mir das Programm genau­er anse­hen. Das Entwicklerteam The low-tech guys hat mir dafür freund­li­cher­wei­se eine Kopie von Crank zur Verfügung gestellt. Dass Crank mit dem Slogan „Effortless macOS auto­ma­ti­on – no manu­al requi­red“ („Mühelose Automatisierung unter macOS – ganz ohne Anleitung“) bewor­ben wird, mach­te mich zusätz­lich neu­gie­rig.

Crank bie­tet mehr als 30 Trigger. Sie reagie­ren auf Änderungen im System, zum Beispiel auf die Anmeldung an ein WLAN-Netz, das Anschließen eines USB-Gerätes, einen neu­en Inhalt in der Zwischenablage oder den Wechsel des Fokus-Modus. Als Reaktion dar­auf kann Crank fol­gen­de Aktionen aus­füh­ren:

  • Benachrichtigen: Mitteilung anzei­gen
  • Öffnen: App star­ten, URL öff­nen, Datei öff­nen
  • Automatisieren: Kurzbefehl, AppleScript oder Shell-Skript aus­füh­ren (zsh, Python 3, Ruby, Perl, Swift oder ein eige­ner Interpreter)
  • Crank steu­ern: Profil akti­vie­ren, Regel akti­vie­ren oder deak­ti­vie­ren

Das Programm kos­tet zum Zeitpunkt die­ses Tests als Einmalkauf 10,00 € (Oktober 2026) statt des regu­lä­ren Preises von 14,28 €. Außerdem lässt es sich 14 Tage kos­ten­los tes­ten. Vorausgesetzt wird macOS 14.0 (Sonoma) oder neu­er.

Das User Interface

Das Bild zeigt eine Benutzeroberfläche einer Software, die für die Automatisierung von Aufgaben verwendet wird. Auf der linken Seite gibt es eine Menüleiste mit verschiedenen Kategorien wie "Hardware", "SENSOR", "Konnektivität" und "Displays". Innerhalb dieser Kategorien sind spezifische Ereignisse und Auslöser aufgelistet. Auf der rechten Seite sind Regeln angezeigt, die festlegen, was passieren soll, wenn bestimmte Bedingungen erfüllt sind. Es gibt auch Einstellungen und Tasten für die Verwaltung dieser Regeln. Die Oberfläche hat ein dunkles Farbschema und einige Punkte sind nummeriert, um spezifische Bereiche oder Funktionen hervorzuheben.

Das UI ist rela­tiv selbst­er­klä­rend, wenn auch nicht ganz Apple-kon­form. In der Titelleiste befin­den sich eini­ge Funktionen. Mit den Schaltflächen unter (1) kann man den lin­ken Triggerbereich ein- und aus­klap­pen, die Einstellungen öff­nen (⌘,), die Befehlspalette auf­ru­fen (⌘K) und die Darstellung der Regeln (6) zwi­schen der hier gezeig­ten Listenansicht und einer Tabellenansicht umschal­ten.

Mit dem Umschalter unter (2) wech­selt man zwi­schen drei Ansichten, der „Trigger“-Ansicht, dem Event-Log (⌘2) und der „Action History“ (⌘3). Die Ansicht „Trigger“ ist die Hauptansicht. Sie besteht aus der Trigger-Liste (1) und dem Regelbereich (6), der die Regeln als Liste von Regelboxen zeigt. Im Event-Log wer­den alle Ereignisse ange­zeigt, die das System aus­löst und auf die Crank ach­tet. In der Action History wer­den die aus­ge­führ­ten Aktionen samt ihrer Ausgaben und etwa­iger Fehler auf­ge­lis­tet.

Der Button unter (3) ist ein Menü und zeigt den Namen des akti­ven Profils an. Aufgeklappt bie­tet es Funktionen, um Profile zu ver­wal­ten und neue Profile anzu­le­gen, in denen dann neue Regeln erstellt wer­den kön­nen. Mit Profilen lässt sich zum Beispiel fest­le­gen, dass am Arbeitsplatz ein ande­res Regelset aktiv ist als zu Hause. Über das Kontextmenü (rech­te Maustaste) einer Regel kann man sie in ein ande­res Profil ver­schie­ben.

Im Bereich (4) ste­hen die Funktionen der Werkzeugleiste, die hier ver­grö­ßert dar­ge­stellt ist:

Auf dem Bild ist eine Reihe von Symbolen zu sehen, die als Benutzeroberfläche dienen. Diese Symbole sind durch rote Zahlen gekennzeichnet und könnten verschiedene Funktionen repräsentieren. Doe Bedeutung wird im Text erklärt
  1. Alle Regeln akti­vie­ren
  2. Alle Regeln deak­ti­vie­ren
  3. Alle Gruppen aus­klap­pen
  4. Alle Gruppen ein­klap­pen
  5. „DryRun“: Versetzt das gesam­te System in einen Testmodus, in dem Crank wei­ter­hin die Bedingungen der Regeln prüft und das Ergebnis in der Action History pro­to­kol­liert, die Aktionen aber nicht aus­führt
  6. Das Löschen einer Regel rück­gän­gig machen
  7. Die gesam­ten Einstellungen als Backup spei­chern, nicht mit dem Teilen-Button ver­wech­seln
  8. Ein Backup wie­der laden

Das Event-Log

Das Bild zeigt ein Event-Log von Crank, die Bildschirm Elemente mit den Zahlen werden im Text erklärt

Das Event-Log (2) ermög­licht einen Überblick über die mög­li­chen Ereignisse, auf die Crank reagie­ren kann, wenn unter (1) „All Triggers“ aktiv ist. Zum Debuggen las­sen sich die Einträge mit „Enabled Rules Only“ auf die Ereignisse beschrän­ken, für die akti­ve Regeln exis­tie­ren.

Im Dropdown-Menü (3) kann das Log nach den Trigger-Bereichen gefil­tert wer­den. Der Papierkorb (5) leert das Log, und über die Suche (6) kann man gezielt nach einem Eintrag suchen.

Die Action History

Das Bild zeigt die Activity History von Crank, die Bildschirm Elemente mit den Zahlen werden im Text erklärt

In der Action History wer­den die aus­ge­führ­ten Aktionen pro­to­kol­liert. Jeder Eintrag nennt die Uhrzeit, die Regel, den aus­lö­sen­den Trigger und die Aktion. Läufe, die im DryRun ent­stan­den sind, sind mit „DRY RUN“ gekenn­zeich­net. Klappt man einen Eintrag auf (3), sieht man die Ausgaben des Skripts, den Exit-Code mit der Laufzeit und die Werte des aus­lö­sen­den Ereignisses.

Die rote Schrift ist dabei nicht unbe­dingt ein Fehler. Rot dar­ge­stellt wird die Standardfehlerausgabe (stderr) des Skripts, und die ent­hält auch nor­ma­le Statusmeldungen, hier etwa den neu­en Namen, unter dem der Screenshot abge­legt wur­de. Ob eine Aktion erfolg­reich war, zeigt der Exit-Code. Eine 0 (grün) steht für Erfolg, alles ande­re (rot) für einen Fehler. So wur­de das Skript im unte­ren Eintrag nach 30 Sekunden abge­bro­chen („Script timed out after 30 seconds“, Exit ‑1).

Das Warndreieck (1) fil­tert die Einträge nach Aktionen, die nicht mit dem Exit-Code 0 enden, die also nicht zum gewünsch­ten Ergebnis geführt haben. Der Papierkorb (1) leert die Liste, und über die Suche (2) las­sen sich ein­zel­ne Einträge fin­den.

Der Aufbau von Regeln

Das Bild zeigt die Trigger-Ansicht mit einer Regel in Crank, die Bildschirm Elemente mit den Zahlen werden im Text erklärt

Eine Regel beginnt immer mit einem Trigger, der zunächst in der lin­ken Seitenleiste (1) aus­ge­wählt wird. In die­sem Beispiel ist das „File/Folder chan­ges“ → „crea­ted“. Mit der Auswahl wird die Regel neu ange­legt und die ers­te Zeile „WHEN“ (2) erstellt. Sie legt fest, dass auf Ereignisse reagiert wer­den soll, die Änderungen im Dateisystem mel­den. Obwohl die Seitenleiste an die­ser Stelle von „Files“ spricht, reagiert der „File Watcher“ auch auf Ordner, die erstellt, geän­dert oder gelöscht wer­den.

In der nächs­ten Zeile „IF“ (3) wird eine Bedingung fest­ge­legt, die genau­er ein­grenzt, auf wel­che Dateien oder Ordner geach­tet wer­den soll. Im Beispiel muss der Dateiname den Text „Bildschirm“ ent­hal­ten.

Die Zeile „THEN“ (4) legt fest, wel­che Aktion aus­ge­führt wer­den soll, etwa ein Kurzbefehl, ein AppleScript, ein Skript oder ein­fach nur eine Mitteilung. Im Beispiel ist es „Run Shell Script“. Der Timeout, der hier ange­zeigt wird (im Beispiel eine Minute), wird unter der Skript-Box (5) ein­ge­stellt.

In der fol­gen­den Zeile (6) wird der Ort fest­ge­legt, der beob­ach­tet wer­den soll („Watch Path“), und ob auch Unterordner ein­be­zo­gen wer­den („Recursive“).

Soll, wie in die­sem Beispiel, ein Skript aus­ge­führt wer­den, lässt sich über (7) der Interpreter aus­wäh­len. Neben zsh, Python 3, Ruby, Perl und Swift kann auch ein eige­ner Interpreter ange­ge­ben wer­den.

In der Box (8) wird das Skript ein­ge­ge­ben. Rechts dane­ben (9) ste­hen die Variablen, die im Skript genutzt wer­den kön­nen, etwa $fileName und $filePath. Auf den Button am unte­ren Rand der Skript-Box („Generate code“) gehe ich wei­ter unten ein.

Je nach Interpreter erschei­nen zusätz­li­che Optionen. Für ein zsh-Skript lässt sich über die Checkbox „Load ~/.zshrc“ (10) fest­le­gen, ob die per­sön­li­che Konfigurationsdatei der Shell mit­ge­la­den wer­den soll. Mit „Run“ (10) kann das Skript direkt aus­ge­führt wer­den. Ein Swift-Skript kann auf Wunsch auch kom­pi­liert wer­den, was ver­mut­lich erst sinn­voll ist, wenn das Skript fer­tig ist, aber dann die Ausführung deut­lich beschleu­nigt.

Unterhalb der Skript-Box befin­den sich Einstellungen, wel­che die Ausführung des Skripts beein­flus­sen. Der Timeout (5) stellt sicher, dass ein Skript, das aus irgend­ei­nem Grund zu lan­ge läuft, abge­bro­chen wird. So kann ein hän­gen­des Skript nicht end­los wei­ter­lau­fen. Wird der Timeout über­schrit­ten, steht in der Action History „Script timed out“ mit dem Exit-Code ‑1, wie im Beispiel oben.

Darunter fol­gen die Einstellungen „Rate Limit“, „Schedule“ und „Conditions“ (11). Im Dropdown-Menü „Rate Limit“ lässt sich ein­stel­len, wie sich die Regel ver­hal­ten soll, wenn ein Ereignis mehr­mals schnell hin­ter­ein­an­der aus­ge­löst wird. Ein Beispiel ist das Schließen eines MacBooks. Dabei wird das Ereignis „LidAngle“ aus­ge­löst, das sich mehr­fach wie­der­ho­len kann. Mit der Option „Throttle“ wird die Aktion schon beim ers­ten Auftreten aus­ge­führt, und alle wei­te­ren Ereignisse wer­den für ein fest­ge­leg­tes Zeitfenster igno­riert. Mit „Debounce“ war­tet die Regel dage­gen eine fest­ge­leg­te Zeit ab, bevor die Aktion aus­ge­führt wird. Im Fall von „LidAngle“ erscheint mir „Debounce“ sinn­vol­ler, weil die Aktion dann erst nach der Wartezeit aus­ge­führt wird und nicht schon bei der ers­ten Änderung des Winkels. (Ausprobiert habe ich das nicht.)

Mit der Option „Schedule“ lässt sich fest­le­gen, an wel­chen Wochentagen und in wel­chem Zeitfenster die Regel aktiv sein soll. Unter dem Schalter „Conditions“ kön­nen außer­dem wei­te­re Bedingungen defi­niert wer­den, die zum Trigger gehö­ren. Achtet der File Watcher wie im Beispiel auf einen bestimm­ten Dateinamen (3) in einem fest­ge­leg­ten Ordner (6), kann hier zusätz­lich ein­ge­stellt wer­den, dass nur Änderungen an Ordnern, nicht an Dateien, berück­sich­tigt wer­den. Die Bedingungen sind damit auf drei Stellen ver­teilt, näm­lich den Dateinamen in der IF-Zeile (3), den Ort im „Watch Path“ (6) und wei­te­re unter „Conditions“. Warum sie nicht an einer Stelle zusam­men­lau­fen, erschließt sich mir nicht.

Mit der letz­ten Option (12), „Also run an action when the file is dele­ted“, kann zusätz­lich eine Aktion für das gegen­tei­li­ge Ereignis defi­niert wer­den, hier das Löschen der Datei. Wird der Schalter umge­legt, erscheint im obe­ren Teil der Regel eine zwei­te „WHEN“-Zeile mit dem gegen­tei­li­gen Trigger („file dele­ted“), für den eine eige­ne Aktion fest­ge­legt wer­den kann (sie­he Bild). Auch hier erschließt sich mir nicht, war­um der Schalter unten in der Regel plat­ziert ist und nicht dort, wo anschlie­ßend die Definition statt­fin­det.

Das Bild zeigt eine Benutzeroberfläche für eine Regel, die mit einem Dateiüberwachungsprogramm verbunden ist. Es gibt Felder für Bedingungen, bei denen eine Aktion ausgeführt wird, wenn eine Datei erstellt oder gelöscht wird. Konkrete Begriffe wie „File Watcher“, „file created“, „file deleted“ und „Run Shell Script“ sind sichtbar. Die Hintergründe sind in einem dunklen Farbschema gehalten.

Regeln und Skripte mit KI erstellen

Eine Regel zu erstel­len ist ziem­lich ein­fach. Zuerst wird der pas­sen­de Trigger aus­ge­sucht, dann die Bedingung fest­ge­legt und schließ­lich eine Aktion gewählt. Einfache Aktionen, etwa eine Mitteilung, wenn der Akkustand unter einen bestimm­ten Wert sinkt, oder das Öffnen einer URL beim Start des Rechners, las­sen sich schnell zusam­men­kli­cken. Wird die Aktion kom­ple­xer, wird es auf­wen­di­ger. Der viel­leicht ein­fachs­te Weg ist dann ein Apple Kurzbefehl als Aktion, denn mit macOS 27 unter­stützt Apple des­sen Erstellung mit­hil­fe von „Apple Intelligence“. Es genügt, zu beschrei­ben, was der Kurzbefehl tun soll. Den Rest erle­digt Apple Intelligence.

Der Code-Generator in Crank

Crank geht mit dem ein­ge­bau­ten Code-Generator einen ähn­li­chen Weg. In den Einstellungen unter „Advanced“ kann ein kos­ten­lo­ser API-Key von Google Gemini ein­ge­tra­gen wer­den. Den Key legt man mit einem kos­ten­lo­sen Google-Account auf aistudio.google.com/api-keys an. Alternativ kann ChatGPT über Apple Intelligence genutzt wer­den. Dazu instal­liert Crank Apple Kurzbefehle, die als Schnittstelle die­nen.

Das Bild zeigt einen Screenshot der Einstellung für die Verwendung von Gemini KI in Crank.

Bei einer neu­en Regel gibt man nun statt eines Skripts eine Anweisung ein, hier mit der Bitte, ein Swift-Skript zu erstel­len:

Create a Swift script that speaks the file name and the path when a new file is created in the folder, in German: Die Datei wurde im Pfad erstellt
Das Bild zeigt, mit de, Prompt im Skript Feld

Nach einem Klick auf „Generate code“ wird das Skript erstellt. In die­sem Beispiel unter Verwendung von mei­nes kos­ten­lo­sen ChatGPT Account über die „Apple Intelligence“ Schnittstelle, weil der Aufruf der Gemini-API in mei­nem kos­ten­lo­sen Account immer mal wie­der wegen Überlastung abge­bro­chen wird.
.
Als Ergebnis wird, nach­dem man die Ausführung des Kurzbefehls geneh­migt hat, das fer­ti­ge Skript anstel­le des Prompts ein­ge­tra­gen:

#!/usr/bin/swift
import Foundation

let env = ProcessInfo.processInfo.environment
guard let fileName = env["fileName"], let filePath = env["filePath"] else {
    exit(1)
}

let message = "Die Datei \(fileName) wurde im Pfad \(filePath) erstellt"

let task = Process()
task.launchPath = "/usr/bin/say"
task.arguments = [message]
task.launch()
task.waitUntilExit()

exit(0)

Crank per MCP steuern

Die zwei­te Möglichkeit besteht dar­in, dass eine KI wie Claude, ent­we­der in der Desktop-App oder mit Claude Code im Terminal, Crank direkt steu­ert. Dazu muss zunächst in den Einstellungen unter „General“ das „crank com­mand line tool“ instal­liert wer­den.

Das Bild zeigt den generellen Einstellungsdialog, wobei die Installation für "Crank command line tool" wichtig ist

Anschließend lässt sich unter „AI agents“ der Crank MCP-Server star­ten („Enable MCP“), der mit einem KI-Agenten ver­bun­den wird, hier mit Claude Code und der Claude-Desktop-App. MCP („Model Context Protocol“) ist eine Schnittstelle, über die KI-Anwendungen exter­ne Werkzeuge anspre­chen kön­nen. Crank unter­stützt außer­dem wei­te­re KI-Agenten, etwa Codex von OpenAI, die Copilot CLI von GitHub, OpenCode und die Gemini CLI von Google.

Bild zeigt den Einstellungsdialog für die MCP Funktionalität

Damit las­sen sich Regeln per Prompt direkt in der App oder im Terminal-Tool des jewei­li­gen KI-Anbieters erstel­len. Claude ver­bin­det sich dazu mit Crank, holt sich die nöti­gen Informationen, erstellt den Code und tes­tet ihn.

Auf dem Bild ist ein Textabschnitt zu sehen, der sich mit der Einrichtung einer automatischen Ankündigungsregel für neue Dateien auf dem Desktop befasst. Die Regel überwacht einen bestimmten Desktop-Ordner und gibt eine Sprachausgabe aus, die den Namen, den Pfad und den Dateityp einer neu erstellten Datei ansagt. Der Text enthält technische Details zu der Regel, die Spracheinstellung (Deutsch) und Beispiele für die Sprachausgabe. Es wird erwähnt, dass die Regel keine Begrenzung für die Anzahl der Ankündigungen hat und nur Dateien im Desktop-Ordner und keine Unterordner betrachtet werden.

Am Ende wird die fer­ti­ge Regel direkt in Crank ange­legt, hier mit der Bezeichnung „Desktop: neue Datei ansa­gen“.

Das Bld zeigt das Ergebnis der Regelgenerierung durch Claude mit dem MCP Anschluss

So kann selbst jemand, der die Syntax einer Skriptsprache nicht kennt, auch kom­ple­xe­re Skripte erstel­len las­sen. Etwas Programmiererfahrung soll­te aber vor­han­den sein, um zumin­dest grob zu ver­ste­hen, was das Skript macht.

Eigene Trigger per Skript

Reichen die vor­han­de­nen Trigger nicht aus, bie­tet Crank die Möglichkeit, einen eige­nen Trigger zu schrei­ben. Statt auf ein Systemereignis zu ach­ten, führt Crank dazu in regel­mä­ßi­gen Abständen ein Skript aus und ent­schei­det anhand des Exit-Codes, ob die Regel aus­löst. Als Beispiel habe ich mir von Claude als KI-Agent eine Regenwarnung umset­zen las­sen. Das Skript fragt für einen fest­ge­leg­ten Ort die Regenprognose der nächs­ten zwei Stunden ab. Wird Regen erwar­tet, erscheint eine Mitteilung, die angibt, wann der Regen beginnt und wie viel zu erwar­ten ist. Das Skript sieht so aus:

import json, sys, urllib.request
from datetime import datetime

# Koordinaten für Heidelberg
LAT, LON = 49.40, 8.68
THRESHOLD_MM = 0.5   # Summe der nächsten 2 Stunden

URL = (f"https://api.open-meteo.com/v1/forecast?latitude={LAT}&longitude={LON}"
       "&minutely_15=precipitation&forecast_minutely_15=8&timezone=Europe%2FBerlin")

# 1 Daten holen, bei jedem Fehler still abbrechen
try:
    with urllib.request.urlopen(URL, timeout=5) as r:
        data = json.load(r)
    times = data["minutely_15"]["time"]
    rain = data["minutely_15"]["precipitation"]
except Exception:
    sys.exit(1)

# 2 Entscheiden, Exit 1 bedeutet nichts tun
total = sum(x or 0 for x in rain)
if total < THRESHOLD_MM:
    sys.exit(1)

# 3 Beginn des Regens und Wartezeit bestimmen
first = next((t for t, x in zip(times, rain) if (x or 0) > 0), times[0])
minutes = max(0, int((datetime.fromisoformat(first) - datetime.now()).total_seconds() // 60))

# 4 Daten für die Aktion ausgeben
print(f"mm={total:.1f}")
print(f"start={first[11:16]}")
print(f"minutes={minutes}")

Eingetragen wird das Skript beim Trigger „Script“. Crank führt es alle 600 Sekunden aus („Poll every“). Endet es mit dem Exit-Code 0, löst der Trigger aus, bei jedem ande­ren Exit-Code wird die Regel über­sprun­gen. Zeilen im Format schlüssel=wert, die das Skript aus­gibt, ste­hen der Aktion als Platzhalter {{schlüssel}} zur Verfügung.

Beispiel des Bildschirmes mit dem eigenen Triggers

Was macht das Skript?

Im Kopfbereich wer­den die benö­tig­ten Bibliotheken gela­den und Variablen defi­niert, näm­lich die Geokoordinaten (hier Heidelberg) und der Schwellenwert, ab dem Regen gemel­det wird.

Die Variable URL ent­hält die Adresse des Wetterdienstes Open-Meteo, über die die Regenmengen der nächs­ten zwei Stunden abge­ru­fen wer­den. Der Parameter minutely_15=precipitation legt fest, dass die Niederschlagsmenge abge­ru­fen wer­den soll, und forecast_minutely_15=8 for­dert acht Werte im Abstand von 15 Minuten an, also zwei Stunden.

import json, sys, urllib.request
from datetime import datetime

# Koordinaten für Heidelberg
LAT, LON = 49.40, 8.68
THRESHOLD_MM = 0.5   # Summe der nächsten 2 Stunden

URL = (f"https://api.open-meteo.com/v1/forecast?latitude={LAT}&longitude={LON}"
       "&minutely_15=precipitation&forecast_minutely_15=8&timezone=Europe%2FBerlin")

Als Nächstes wer­den die Wetterdaten über die URL abge­ru­fen. Die Antwort von Open-Meteo ist ein JSON mit den Vorhersagedaten der nächs­ten zwei Stunden in 15-Minuten-Schritten. Daraus wer­den die Uhrzeiten in der Liste timesund die Niederschlagsmengen in der Liste rain abge­legt, wobei die Einträge an der­sel­ben Position zusam­men­ge­hö­ren. Schlägt der Aufruf fehl, etwa weil der Dienst nicht erreich­bar ist oder nicht inner­halb von fünf Sekunden ant­wor­tet, wird das Skript mit dem Exit-Code 1 been­det.

# 1 Daten holen, bei jedem Fehler still abbrechen
try:
    with urllib.request.urlopen(URL, timeout=5) as r:
        data = json.load(r)
    times = data["minutely_15"]["time"]
    rain = data["minutely_15"]["precipitation"]
except Exception:
    sys.exit(1)

Danach wird berech­net, ob in den nächs­ten zwei Stunden min­des­tens 0,5 mm Regen fal­len. Dazu wer­den alle Werte der Liste rain addiert und in der Variablen total gespei­chert. Erreicht total den Schwellenwert nicht, wird das Skript wie­der­um mit dem Exit-Code 1 been­det, und die Regel löst nicht aus.

# 2 Entscheiden, Exit 1 bedeutet nichts tun
total = sum(x or 0 for x in rain)
if total < THRESHOLD_MM:
    sys.exit(1)

Anschließend wird der ers­te Zeitpunkt inner­halb der zwei Stunden ermit­telt, zu dem laut Vorhersage Regen ein­setzt. Dazu fasst die Funktion zip(times, rain) die Werte aus bei­den Listen „posi­ti­ons­wei­se“ zu Paaren zusam­men, jeweils eine Uhrzeit mit dem pas­sen­den Niederschlagswert. next(...) nimmt dar­aus den ers­ten Zeitpunkt, zu dem der Niederschlagswert grö­ßer als null ist, und spei­chert ihn in first. Danach wird berech­net, wie vie­le Minuten bis zu die­sem Zeitpunkt ver­ge­hen. Liegt er bereits in der Vergangenheit, wird die Wartezeit auf null gesetzt.

# 3 Beginn des Regens und Wartezeit bestimmen
first = next((t for t, x in zip(times, rain) if (x or 0) > 0), times[0])
minutes = max(0, int((datetime.fromisoformat(first) - datetime.now()).total_seconds() // 60))

Im letz­ten Schritt gibt das Skript die Werte für die Mitteilung als Schlüssel-Wert-Paare aus, damit die Aktion „Show Notification“ sie ver­wen­den kann.

# 4 Daten für die Aktion ausgeben
print(f"mm={total:.1f}")
print(f"start={first[11:16]}")
print(f"minutes={minutes}")

Diese Werte las­sen sich in den Feldern „Title“ und „Body“ der Mitteilung als Platzhalter ein­set­zen, hier {{minutes}}, {{mm}} und {{start}}.

Das Bild zeigt einen Benutzeroberflächen-Teil, der Informationen über bevorstehenden Regen anzeigt. Es gibt zwei Textfelder: Eines mit dem Titel "Regen in {{minutes}} Minuten" und ein anderes mit dem Text "{{mm}} mm erwartet, ab {{start}} Uhr". Dabei scheinen Platzhalter verwendet zu werden, um spezifische Zeit- und Mengenangaben zu ergänzen.

Sobald Regen vor­her­ge­sagt wird, erscheint eine Mitteilung wie die­se.

Bild zeigt eine entsprechend Mitteilung

Bei anhal­ten­dem Regen kommt es auf den Schalter „Trigger on“ an.

Bild zeigt die Einstellung für den Trigger (State Changes oder Every Run)

Steht der Schalter auf „State Changes“, gibt es nur dann eine Meldung, wenn zum ers­ten Mal Regen inner­halb der nächs­ten zwei Stunden vor­her­ge­sagt wird. Jeder wei­te­re Aufruf endet zwar wei­ter­hin mit dem Exit-Code 0, führt aber nicht zu einer neu­en Mitteilung. Erst wenn die Vorhersage kei­nen nen­nens­wer­ten Regen mehr ent­hält oder der Dienst nicht erreich­bar ist, endet das Skript wie­der mit dem Exit-Code 1. Dadurch wird ein inter­ner Merker zurück­ge­setzt, und beim nächs­ten Aufruf mit dem Code 0 erscheint wie­der eine Mitteilung.

Steht der Schalter auf „Every Run“, erscheint bei jedem Aufruf, also alle zehn Minuten, eine neue Mitteilung mit aktua­li­sier­ten Daten, solan­ge Regen vor­her­ge­sagt wird. Erst wenn kein Regen mehr erwar­tet wird, blei­ben die Mitteilungen aus. In bei­den Fällen gilt das nur, wenn min­des­tens 0,5 mm Regen vor­her­ge­sagt wer­den.

Das ist eine ein­fa­che Lösung, die nur davor warnt, dass es irgend­wann in den nächs­ten zwei Stunden reg­nen wird. Ein Hinweis, wann der Regen wie­der auf­hört, fehlt. Ihn zu ergän­zen wür­de hier aber den Rahmen spren­gen.

Zusammenfassend las­sen sich mit einem Skript-Trigger eige­ne Ereignisse defi­nie­ren, etwa um feh­len­de Sensoren des Systems abzu­fra­gen oder einen belie­bi­gen exter­nen Dienst ein­zu­bin­den.

Regeln teilen

Regeln las­sen sich mit ande­ren Crank-Nutzern tei­len. Dazu klickt man oben rechts an der Regelbox auf das macOS-typi­sche Teilen-Symbol. Dadurch wird ein Link in die Zwischenablage kopiert, der unge­fähr so aus­sieht (hier die Regenwarnung als Beispiel):

https://crank.lowtechguys.com/rule/YnZ4bvEEAACvAwAA4Ah7IjMiOiJub3RpZmljYXRpb24iLCIxMDQAFeADXC91c3JcL2JpblwvcHl0aG9uMwAcQBp2QAgxgAk0MeALe3ttbX19IG1tIGVyd2FydGV0LCBhYiB7e3N0AA4AGsAsVWhyQCsy73NjcmlwdFRyaWdnZXJlZAhCRjDoUmVnZW4gaW4ANOdtaW51dGVzADZQCk0Iikg7NuAZaW1wb3J0IGpzb24sIHN5cywgdXJsbGliLnJlcXVlc3RcbmZyb20gZGEAgORpbWUgIDAoEOAYXG5cbkxBVCwgTE9OID0gNDkuNDAsIDguNjhcblRIUkVTSE9MRF9NTQAc6zAuNSAgICMgU3VtAEjqZGVyIG7DpGNocwCN6iAyIFN0dW5kZW4IUMAvVVJM4CAoZlwiaHR0cHM6XC9cL2FwaS5vcGVuLW1ldGVvLmNvbVwvdjFcL2ZvcmVjYXN0P2wBYOZ0dWRlPXsAj-Z9JmxvbmcoEOhPTn1cIlxuIBgByQ1cIiby52x5XzE1PXAAQORpcGl0EZ9oTyZoI1_0iOo4Juh6b25lPUV1cgCG5yUyRkJlcmwBvIixIinmIyAxIERhCMjkaG9sZQFI7mJlaSBqZWRlbSBGZWhsAOrlc3RpbGwBvkBfYkjmaOR0cnk6GJPkd2l0aDl09UAPLgjtQQooiH0sIOxvdXQ9NSkgYXMgcjowygmFybxhID3x6C5sb2FkKHIpGB0IM8ghcyA98cjNW1wi98ARXCJdEbqYLSJd5HJhaW44LPABOSHzEDXoZXhjZXB0IEUQBwAVIJICPecuZXhpdCgxIRXmMiBFbnRzAPCJFWlkQBxFASlBGGIAYut0IG5pY2h0cyB0dQkP5G90YWwAhetzdW0oeCBvciAwIAGLir0geAigAFLIJmlmIPKaWTwg9jh9_ugzIEJlZ2lubgJpkwlzIIJicyCLSiBXgIt6ZUFqYsmOaW1t5WZpcnN0AH8A2-R0KCh0EHyYf3Qs5HppcCgRUFiKLECJICCkwAUpID4ZouRzWzBdAHsZPwl85G1heCgDC4BSaW4rLktMLsBgaXNv5G1hdCgQd8tOKSAt9egubm93KCkpLhDp619zZWNvbmRzKCkgCwLkIDYwKSDcYm4062bDvHIgZGllIEFrCYjnIGF1c2dlYgjRiHhwcgNC5G1tPXsQTuU6LjFmfRKwMBwUSJCGPXvnWzExOjE2XTgi9SDObD898QAfDDlGM-U2MDAuMAzQNIDiIn0GAAAAAAAAAGJ2eCQ

Die Regel wird dabei nicht auf einem Server gespei­chert. Sie steckt kom­plett im Link, und zwar kom­pri­miert und Base64-kodiert, aber nicht ver­schlüs­selt. Wer den Link hat, kann ihren Inhalt also lesen. Der Server dient offen­bar nur als Brücke. Er ersetzt den Teil https://crank.lowtechguys.com/ durch crank://, sodass die Regel bei einem Aufruf sofort in Crank in einem Import-Fenster erscheint.

Vor dem Import soll­te man unbe­dingt kon­trol­lie­ren, was die Regel macht. Eine geteil­te Regel kann Shell-Skripte auf dem Rechner aus­füh­ren. Dabei kann es je nach Sicherheitseinstellungen auch ohne Rückfrage gesche­hen. Alles, was in einer Regel steht, steckt auch im Link, etwa Pfade, Orte oder Zugangsdaten. Das soll­te man vor dem Teilen beden­ken.

Wer den Server umge­hen möch­te, kann den Link auch selbst bear­bei­ten und https://crank.lowtechguys.com/ durch crank:// erset­zen. Gibt man die­se Adresse in einen Browser ein, wird die Regel genau­so impor­tiert.

Ich weiß nicht, war­um die Entwickler die­sen Weg gewählt haben, statt die Regel ein­fach als Datei zu spei­chern, die sich dann tei­len lie­ße.

Fazit

Crank reiht sich in eine Reihe von Werkzeugen ein, die dabei hel­fen, bestimm­te Abläufe zu auto­ma­ti­sie­ren. Es besticht durch die ein­fa­che Umsetzung des Ansatzes Trigger → Bedingung → Aktion, durch die gro­ße Zahl an Triggern und durch die unter­stütz­ten Skriptsprachen. Für ein­fa­che Aktionen las­sen sich Apple Kurzbefehle nut­zen, zsh eig­net sich für vor­han­de­ne Kommandozeilenwerkzeuge, Python für Webdienste und JSON-Verarbeitung und ein Swift-Skript glänzt bei Aktionen, die Zugriff auf macOS-Frameworks benö­ti­gen. Wer eine ande­re Sprache bevor­zugt, kann sogar einen eige­nen Interpreter ein­bin­den.

Es gibt aber auch eini­ge Kritikpunkte.

  • Schalter lie­gen oft weit von dem ent­fernt, was sie steu­ern. Der Schalter „Also run an action when the file is dele­ted“ steht unten in der Regel, die zuge­hö­ri­ge Aktion erscheint aber im obe­ren Teil.
  • Bedingungen sind auf meh­re­re Stellen ver­teilt, näm­lich auf die IF-Zeile, den „Watch Path“ und den Schalter „Conditions“.
  • Regeln las­sen sich in der Liste nicht frei ver­schie­ben. Die Gruppen bie­ten zwar etwas Ordnung, ein freie­res Sortieren wäre aber hilf­reich.

Profile sind eine prak­ti­sche Möglichkeit, Regeln je nach Arbeitskontext zusam­men­zu­stel­len. Schade ist nur, dass sich Regeln per Kontextmenü zwi­schen Profilen ver­schie­ben, aber nicht kopie­ren las­sen.

Für ein­fa­che Anwendungen ist das Programm eini­ger­ma­ßen intui­tiv zu bedie­nen. Es gibt aber auch kom­ple­xe­re Konzepte, die sich nicht durch blo­ßes Hinsehen erschlie­ßen, etwa „State Changes“ gegen­über „Every Run“ oder „Throttle“ gegen­über „Debounce“. Hier wäre mehr Dokumentation hilf­reich, auch wenn der Slogan „no manu­al requi­red“ lau­tet.

Wer einen ein­fa­chen Weg sucht, Skripte abhän­gig von Systemereignissen oder zeit­ge­steu­ert aus­zu­füh­ren, kann mit Crank sehr schnell zum Ziel kom­men. Allerdings ist ein Mindestmaß an Programmierkenntnissen not­wen­dig. Selbst wenn nur fer­ti­ge Regeln aus dem Internet oder eine KI für die Erstellung genutzt wer­den, soll­te man so viel Wissen mit­brin­gen, dass man zumin­dest grob ver­steht, was das Skript macht.

Fediverse-Reaktionen

Schreibe einen Kommentar

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