Diary
Turn a day of synced Garmin and Zwift data into one Obsidian daily note, merging into any note you have already edited. WHAT IT DOES
Your fitness data already lands in swamp — Garmin activities, wellness and
weigh-ins, plus ranked Zwift event picks — but reading it means knowing four
models by name. This extension joins them into one per-day diary note:
activities with durations and totals, sleep and steps, weight plus whatever
body composition your scale measured, the suggested Zwift rides with reasons
and links, a weather line from @svendowideit/bom-weather, and a
## Extensions published list from @svendowideit/extension-releases. It
extends @magistr/obsidian/vault, composing that model's read/create
instead of writing files itself, and never touches the network: collect
reads data other models already synced. The write is marker-based, so
re-running a day rewrites only the machine-managed region and leaves your
prose and ticked checkboxes untouched — making a phone-editable vault safe to
regenerate.
INSTALL
swamp extension pull @svendowideit/diaryDEPENDENCIES
@magistr/obsidian-vault and @svendowideit/extension-releases, pulled
automatically by the single install above. The first supplies the
@magistr/obsidian/vault model this extension extends; the second supplies
the append-only release log the ## Extensions published section reads. The
Garmin (@svendowideit/garmin), Zwift (@svendowideit/zwift) and BOM
(@svendowideit/bom-weather) syncs that produce the source data are not hard
dependencies — the diary reports what is missing when they have not run, and
simply omits the weight, forecast and releases sections when no matching
data exists.
RUN
# One-time: a vault model pointed at the mounted Obsidian directory (the
# headless filesystem backend needs no Obsidian app running).
swamp model create @magistr/obsidian/vault obsidian-vault \
--global-arg vaultRoot=$HOME/Obsidian/Vault
# Optional: a BOM instance named `bom` (the name the bundled bom-weather
# workflow syncs) for your location. Its stored forecast is added to the
# page header. The bundled workflow's 4x-daily trigger carries its own
# location inputs, so point both the trigger and your first run at yours.
swamp model create @svendowideit/bom-weather bom \
--global-arg name=Stafford --global-arg state=QLD
swamp workflow trigger set @svendowideit/bom-weather \
--schedule "30 6,12,18,0 * * *" \
--input name=Stafford --input state=QLD
swamp workflow run @svendowideit/bom-weather \
--input name=Stafford --input state=QLD
# See whether the BOM data exists yet (the `forecast` resource, and the
# instance and its resolved location).
swamp data get bom forecast --json
swamp model get bom --json
# Collect a day from the synced fitness data (defaults to today in
# Australia/Brisbane) and write one collect resource. With `bom` data
# present, the forecast line is added automatically.
swamp model method run obsidian-vault collect
# Render the markdown without touching the vault first, so you can check
# the page shape before publishing.
swamp model method run obsidian-vault render
# Publish: render, merge into the existing daily note between the managed
# markers, and write daily/<date>.md through the vault model.
swamp model method run obsidian-vault publish
# Or run the bundled workflow, which does collect + publish in one go and
# is scheduled hourly at :45 local under 'swamp serve'. It rebuilds today
# and yesterday, so a weigh-in recorded after today's page was first
# written is picked up on the next pass.
swamp workflow run @svendowideit/diary-daily
# Backfill a specific past day instead of today (note it will not fetch data, just use what is in swamp already).
swamp workflow run @svendowideit/diary-daily --input date=2026-10-03CONFIGURE
Every method takes timezone (default Australia/Brisbane), an optional
date (default: today in that timezone), and days (default 1; the
workflow sets 4 to rebuild the last four days so a late-arriving weigh-in
still lands). collect maps data
sources by instance name: activitiesModel (garmin-activities),
healthModel (garmin-health), bodyModel (garmin-body, optional),
recommenderModel (zwift-recommender), eventsModel (zwift-events),
bomModel (bom, optional), releasesModel (extension-releases, optional)
and topN (5). Set bomModel to the name of
your @svendowideit/bom-weather instance; when it holds a forecast for the
day, the header line appears, otherwise it is silently omitted and listed in
collect's missing field. Set bodyModel to the name of your
@svendowideit/garmin-body instance; when it holds a weigh-in for the day a
Weight line is added, otherwise it is omitted and listed in missing.
publish takes vaultModel (obsidian-vault), folder (daily), noteName
and allowDotObsidian. The workflow takes the same names as --input (plus
date, days, folder, topN). See the README for the full argument table.
UPGRADE
The diary installs nothing on the host and owns no model of its own, so
there is no in-place rebuild to run. Upgrade by updating the extension; its
methods attach to the @magistr/obsidian/vault instance you already use, so
the new version applies automatically on the next collect/publish run
and existing notes are only rewritten inside the managed markers.
swamp extension update @svendowideit/diaryWHAT IT INSTALLS
Nothing on the host — no services or daemons. It writes swamp resources
and, through the vault model, the diary note inside your Obsidian vault.
The bundled @svendowideit/diary-daily workflow registers one cron trigger
while swamp serve runs: 45 * * * * (hourly at :45, host-local) — a
quarter-hour after the @svendowideit/fitness-refresh run it consumes.
Downloads and backfills run on demand.
Build today's (and the last few days') Obsidian diary page from already-synced data. Runs hourly at :45, a quarter-hour after the @svendowideit/fitness-refresh pass, and joins what the Garmin and Zwift syncs produced into one note. Each run rebuilds the last `days` local days (default 4: today and the three before), so a weigh-in or activity that lands after a page was first written still updates that page: 1. assert today's source data is fresh enough to bother (Garmin activity
updated dependencies
- Has README or module doc2/2earned
- README has a code example1/1earned
- README is substantive1/1earned
- Most symbols documented1/1earned
- No slow types (deprecated)1/1earned
- Dependencies pass trust audit2/2earned
- Has description1/1earned
- Platform support declared (or universal)2/2earned
- License2/2earned
- Verified public repository2/2earned