Skip to content

Repository files navigation

Pebble Index → Super Productivity, Joplin, Calendar and Telegram

Build

A tray app (Avalonia, .NET 8, Windows + Linux) that turns Pebble Index 01 voice notes into Super Productivity tasks — and, optionally, lets an AI classifier (Claude, Gemini, OpenAI, or a local Ollama model) route each one further: shopping items split one-per-task, notes saved to Joplin, dated items added to Google Calendar, "send a message to X" sent through Beeper.

Pebble Index 01 ──HTTPS──▶ your tunnel ──▶ Index2SP :8787/pebble ──▶ Super Productivity :3876

What it does

  • Every note becomes an SP task — title from the transcription, full text + metadata in notes.
  • SP unreachable? Queued to a local outbox, retried until it lands.
  • Audio with no transcription (Pebble sent audio only)? With Whisper ASR webservice on, a local Whisper ASR webservice (e.g. onerahmet/openai-whisper-asr-webservice:latest) transcribes it instead of the webhook being rejected.
  • With aiClassifier on, the AI also: cleans the task title (strips "add a task to", "remind me to", "send a message to X saying", etc. down to just the content), picks project/tags, splits multi-item shopping lists, sets due dates, and — per destination toggle — files notes to Joplin, adds calendar events, sends a Beeper message, or runs a real web search (via Claude) and sends the summary through a Telegram bot. Beeper sends immediately, with no confirmation step.
  • Uncaught errors are logged and reported as an SP task instead of crashing silently. Integration outages get their own SP task too.
  • Checks GitHub for a newer release and flags it in the tray menu — downloads it on request, nothing runs without you clicking it.

Install

Grab a release.

  • Windows — run the installer, or unzip the portable build.
  • Linux — several options per release: a .tar.gz (extract, run ./install.sh), an .AppImage (make executable, run), a .deb, or a .rpm. Arch users get a PKGBUILD instead of a prebuilt package — makepkg -si it.

Set up

  1. Enable Settings → Misc → Local REST API in Super Productivity; copy its token.
  2. Tunnel the listener: cloudflared tunnel --url http://127.0.0.1:8787 (already run nginx, Caddy, or IIS on a public box instead? see reverse-proxy/).
  3. In Pebble's webhook settings, set the URL to https://<tunnel-host>/pebble.
  4. Send Pebble's test event, then record a real note.

Configure

Tray → Edit config… opens config.json (%APPDATA%\Index2SP\ on Windows, ~/.config/Index2SP/ on Linux); Reload config applies changes. Every destination — Super Productivity, AI classifier, Joplin, Google Calendar, Beeper, Telegram, Whisper — has its own tray submenu: enable, credentials, and Test connection (or Test all connections for one combined check). Full field reference: config.example.json.

Super Productivity → SuperSync fallback creates the task straight on your SuperSync server (hosted or self-hosted) whenever the Local REST API can't be reached — SP closed, laptop asleep — so captures still land and sync to every device instead of waiting in the outbox. Set the server URL, the access token shown on the server's page after you log in, and the same encryption password SP's sync settings use (every op is end-to-end encrypted with it); Test connection checks both. The local API stays the primary path. Fallback tasks with no project go to SP's Inbox rather than your configured default project, and project/tag lists for the AI classifier still come from the local API.

Tokens, API keys, and secrets are stored encrypted (enc:v1:…) with a key in secret.key next to config.json. You can paste a plain value into config.json by hand — it's encrypted on the next launch or Reload config. Back up secret.key with config.json; without it the encrypted values can't be read and have to be re-entered.

AI classifier → Provider picks the backend: Claude, Gemini, OpenAI, or a local Ollama server. Each has its own credential/model submenu; only the selected provider's needs to be filled in. Claude and Gemini's model pickers pull your account's real available models once a key is set (Refresh projects, tags & notebooks re-fetches them, alongside SP/Joplin/Calendar/Ollama — a built-in shortlist shows until the first successful fetch). OpenAI's stays a fixed shortlist. Ollama needs no key — just a server URL and a model, picked from whatever's actually pulled on that host — but, unlike the three hosted providers, can't be forced to call the tool: an unsuited model may just not classify, falling back to the static config like any other failure. The background health check and Test all connections cover whichever provider is currently selected too.

AI classifier → Fallback provider tries a second backend when the primary one fails (no credential, network, timeout, bad response) before giving up and using the static config.

By default a note/event/message is filed to Joplin/Calendar/Beeper and still becomes an SP task. AI classifier → Route to one destination only skips the SP task when that other destination actually succeeds, so you don't get both — falling back to the SP task if it fails.

Two setups need an extra step first:

  • Google Calendar — create a Google Cloud OAuth client (type Desktop app), paste its ID/secret into the tray, then Connect… for a one-time browser sign-in.
  • Beeper — create a personal access token in Beeper Desktop's API settings, paste it in. Naming a platform ("text Abbie on Telegram") picks that chat when the recipient has several; with no platform named, it only sends when the recipient matches exactly one chat.
  • Telegram — message @BotFather to create a bot and get a token, paste it into the tray. For each destination chat, message the bot from it once, then check the bot's getUpdates response (or ask @userinfobot) for that chat's ID — set it under Web search and/or Webhook receipt.

Speech-to-text (Whisper & NVIDIA Parakeet) transcribes audio for webhooks where Pebble sent voice audio without a transcription:

  • Embedded mode (mode: "embedded", default): Runs whisper.cpp directly in-process via Whisper.net with zero external dependencies, no Python, and no Docker. On first run, it automatically downloads your selected model (e.g. tiny.en ~75MB, base.en ~140MB, or small.en ~460MB) and caches it in %APPDATA%\Index2SP\models\ / ~/.config/Index2SP/models/. Includes automatic NoAVX fallback for platforms lacking AVX instructions.
  • Remote mode (mode: "remote"): Connects over HTTP REST to an external speech-to-text server:
    • NVIDIA Parakeet (OpenAI-compatible /v1/audio/transcriptions):
      # GPU (CUDA):
      docker run -d --name parakeet --gpus all -p 5092:5092 ghcr.io/achetronic/parakeet:latest-cuda
      # CPU:
      docker run -d --name parakeet -p 5092:5092 ghcr.io/achetronic/parakeet:latest
      Set baseUrl: "http://127.0.0.1:5092", model: "parakeet-tdt-0.6b", and optional apiKey if secured.
    • Whisper ASR webservice (/asr endpoint):
      docker run -d -p 9000:9000 -e ASR_MODEL=base onerahmet/openai-whisper-asr-webservice:latest
      Set baseUrl: "http://127.0.0.1:9000". When local ffmpeg is available, Index2SP automatically pre-decodes compressed Pebble .m4a files into clean 16kHz mono WAV audio before transcription. It's a fallback only: Pebble's own transcription is always used when present, and this only fires for an audio-only webhook.

Web search ("search the web for...", "google...", "what is the latest version of X") searches via whichever provider is selected for classification — Claude and Gemini each have a real built-in search tool; OpenAI's runs on its separate Responses API. Ollama has none of its own, so it falls back to Claude when a Claude key is set, and skips search otherwise. The summary goes to the chat ID set in Web search → Set Telegram chat ID…. Needs Telegram enabled.

Webhook receipt sends a short Telegram message for every capture — "Webhook Received, Note Created", "Webhook Received, Task Created", etc. — to the chat ID set in Webhook receipt → Set Telegram chat ID…. It's a receipt, not a delivery confirmation: it fires as soon as Index2SP knows what the AI decided, whether or not the underlying task or destination actually succeeds. Needs Telegram enabled.

Telegram only holds the bot connection — web search and webhook receipt each pick their own destination chat ID, so the two can go to different chats (or the same one). Unlike Beeper, there's no per-capture recipient matching: a bot only knows chats it's already been messaged from, so each chat ID is something you set up front, not something spoken. The general "send a message to X" feature stays on Beeper, which is where matching a spoken name against your existing chats actually matters.

Check for updates automatically (on by default, bottom of the tray menu) pings GitHub once a day; Check for updates runs it on demand. A found update can be downloaded straight from the tray — click it again once it's ready to install: Windows launches the installer (still its own click-through UI, and quits Index2SP first since the installer needs to replace the running exe); Linux extracts the tarball and opens the folder so you run ./install.sh yourself. Nothing is ever downloaded or launched without you clicking it.

Build from source

dotnet build -c Release
pwsh ./build.ps1 -Version X.Y.Z        # Windows: installer + portable zip
bash scripts/package-linux.sh X.Y.Z    # Linux: tarballs

CI builds every push/PR; pushing a v* tag cuts a GitHub Release.

Limitations

  • Audio-only webhooks are rejected (422) — no text, no task — unless speech recognition (Parakeet or Whisper) is enabled and transcribes usable text.

  • Duplicate webhooks are caught only when the sender includes an id form field. A repeat of an id still processing gets 409 already processing; a repeat of one already handled gets 200 with duplicate: true and is ignored. Failed attempts release the id so retries go through. Ids are remembered in memory for 24 hours (not across restarts).

  • No recurring tasks or subtasks (the SP REST API doesn't support them).

  • Outbox retries check for an exact title+notes match before recreating a task, which covers a lost reply after Super Productivity actually created it — but not two genuinely separate duplicate files on disk.

  • No OS-level crash-restart — a hard crash stays down until you relaunch (in-process bugs are caught and don't crash the app; see above).

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages