Bambu Bar
Monitor Bambu Lab printers from your Mac menu bar: print progress, the live camera, AMS filament, alerts and desktop widgets. Bambu Bar for macOS 26+.
Multi-account, multi-device availability planning, public booking page + private planning page
Translation pending
ProductivityTypeScript · PythonGitHub @grabskimmOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗Aggregate calendar availability from many accounts — Google, Outlook/M365, iCloud/Fastmail, plus device-bound work accounts behind Conditional Access — into a single private free/busy ICS feed you subscribe to for personal planning. Read-only, private, never shared externally.
Privacy by construction, with source attribution. AvailCal ingests only busy intervals —
start/end/statusplus a single owner-assigned one-word source label. Titles, descriptions, attendees and locations are stripped at the earliest point. The label becomes each event'sSUMMARY(andCATEGORIES) so you can still tell which calendar a block came from — a coarse tag likeWork,Perso,iCloud, never event content.
Cloud-reachable accounts ─(secret ICS URL / CalDAV)─┐
├─► Container Apps Job (cron, scale-to-zero)
Device-bound accounts ─(local agent → /raw JSON)─────┘ pull → normalize → merge → emit
▼
Blob: /raw/*.json + /merged/availability.ics + /raw/*.ics
▼
Apple Calendar / Thunderbird / Fantastical (ICS subscription, hourly)
A scheduled, scale-to-zero job (hourly cron, no always-on server) pulls every
source, normalizes all times to UTC, merges per-source busy blocks, and writes
one merged availability.ics to object storage. Your calendar clients subscribe
to that object. See docs/ARCHITECTURE.md.
It runs on Cloudflare Workers + Containers + R2 (primary — a Worker cron
drives the merge Container and serves the feed from R2) or an Azure Container
Apps Job + Blob (alternative). The same merge/ image runs on both.
Storage is pluggable. The same image writes to Cloudflare R2 (S3-compatible,
zero egress), Azure Blob, or a local directory — selected by env vars
(AVAILCAL_OUTPUT_DIR > R2 > Azure). For R2, see
infra/cloudflare/README.md.
The only bespoke code is the device-bound local reads (agents) and the
free/busy merge. Everything else reuses existing tools: recurring-ical-events
(recurrence), vdirsyncer (CalDAV), icalendar (parse/emit), native OS calendar
stores (device reads), OS schedulers (agents), and Container Apps (cloud timer).
| Path | What |
|---|---|
merge/ | The Python merge job (pull → normalize → merge → emit) + Dockerfile |
agents/windows/ | Outlook COM exporter + Scheduled Task installer |
agents/macos/ | EventKit exporter + launchd agent installer |
infra/ | Bicep for Storage, Key Vault, and the scheduled Container Apps Job |
.github/workflows/ | CI (lint/test/build) and OIDC deploy |
docs/ | Architecture, getting ICS URLs, subscribing, and the runbook |
sources.example.toml | The source registry (rawname → one-word label) |
az) with the Bicep extension.cd merge
pip install -e '.[dev]'
ruff check .
pytest -q
Copy sources.example.toml to sources.toml and map each raw input (ICS feed,
CalDAV account, device calendar name) to a single-word label. Labels must be
unique single tokens — the job validates this at startup and fails fast.
Follow docs/GET-ICS-URLS.md to obtain each account's
secret ICS URL (Google secret-iCal, Outlook published ICS) or set up CalDAV
(iCloud/Fastmail via vdirsyncer). Work accounts that block published ICS use
the device agents instead.
cd merge
AVAILCAL_SOURCES_TOML=../sources.toml \
AVAILCAL_ICS_FEEDS='GoogPersonal=https://…/basic.ics' \
AVAILCAL_OUTPUT_DIR=./out \
python -m availcal.main
# -> ./out/merged/availability.ics (and ./out/raw/<Label>.ics overlays)
Two supported targets — the same merge/ image runs on both:
Cloudflare (Workers + Containers + R2) — primary. A Worker runs the hourly cron, drives the merge Container, serves the feed from R2, and accepts agent uploads. See worker/README.md and infra/cloudflare/README.md.
wrangler r2 bucket create availcal # one-time
cd worker
npm ci
npm run typecheck # tsc --noEmit
npx wrangler types # validate wrangler.jsonc + bindings
# set secrets once (FEED_TOKEN, AGENT_TOKEN, RUN_TOKEN, AVAILCAL_R2_*, AVAILCAL_ICS_FEEDS):
wrangler secret put FEED_TOKEN # …repeat per secret (see worker/README.md)
npx wrangler deploy # builds+pushes the Container, binds R2, registers cron
# local dev: cp .dev.vars.example .dev.vars && npx wrangler dev
CI runs wrangler types + tsc on every PR; npx wrangler deploy runs from
.github/workflows/deploy-cloudflare.yml (manual/tag).
Azure (Container Apps Job) — alternative.
az acr login --name <ACR_NAME>
docker build -t <ACR_LOGIN_SERVER>/availcal:v1 ./merge
docker push <ACR_LOGIN_SERVER>/availcal:v1
./infra/deploy.sh availcal-rg eastus containerImage=<ACR_LOGIN_SERVER>/availcal:v1
Then load feed secrets into Key Vault / Workers Secrets and trigger a run — see docs/RUNBOOK.md.
This is the beginning of the README. Read the rest at the original. Read the full README ↗