Go package and CLI for BIR collection days
  • Go 84.5%
  • HTML 14%
  • Dockerfile 1.5%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Tim Hårek Andreassen eee64c8d0c
All checks were successful
ci / check (push) Successful in 33s
release / check (push) Successful in 28s
release / ci (push) Successful in 0s
release / image (push) Successful in 38s
build: Add compose file and Forgejo CI/release workflows
The image is now built the same way as gavebord's: multi-stage with the
binary staged in /out, a # syntax line and -trimpath. compose.yaml runs
the published image with BIR_BASE_URL, for self-hosting behind a proxy.

CI gates on mise run check (new: gofmt + vet) and the tests; release
builds and pushes the image with kaniko on v* tags.
2026-09-11 18:39:16 +02:00
.forgejo/workflows build: Add compose file and Forgejo CI/release workflows 2026-09-11 18:39:16 +02:00
cmd/bir build: Add compose file and Forgejo CI/release workflows 2026-09-11 18:39:16 +02:00
templates feat: Show upcoming collections per fraction like bir.no 2026-08-28 14:33:15 +02:00
.dockerignore build: Add compose file and Forgejo CI/release workflows 2026-09-11 18:39:16 +02:00
.gitignore refactor: Rewrite everything as CLI using BIR's own API 2026-04-16 21:52:13 +02:00
bir.go feat: Show upcoming collections per fraction like bir.no 2026-08-28 14:33:15 +02:00
bir_test.go fix: Re-login when BIR reports an expired session as 500 2026-08-18 21:41:03 +02:00
compose.yaml build: Add compose file and Forgejo CI/release workflows 2026-09-11 18:39:16 +02:00
Dockerfile build: Add compose file and Forgejo CI/release workflows 2026-09-11 18:39:16 +02:00
go.mod build: Move module path to src.timharek.no/tim/bir 2026-09-09 21:50:55 +02:00
go.sum cmd: Use cobra 2026-04-16 22:07:24 +02:00
LICENSE.md Initial commit 2024-10-05 14:56:10 +02:00
mise.toml build: Add compose file and Forgejo CI/release workflows 2026-09-11 18:39:16 +02:00
README.md build: Add compose file and Forgejo CI/release workflows 2026-09-11 18:39:16 +02:00
server.go feat: Show upcoming collections per fraction like bir.no 2026-08-28 14:33:15 +02:00
server_test.go feat: Show upcoming collections per fraction like bir.no 2026-08-28 14:33:15 +02:00

BIR tømmekalender

CLI tool that generates .ics calendar files from BIR's collection schedule.

This project is not affiliated with BIR or anyone else.

Why does this exist?

I don't want to have to have an app to see when my trash is being collected. A calendar works just fine :)

Usage

Search for your property

bir search "Your Address"

Returns a list of matching properties with their IDs.

Generate calendar file

bir generate <eiendomId>

Writes bir-<eiendomId>.ics to the current directory. Import this file into your calendar app.

Re-running with the same eiendomId produces the same event UIDs, so reimporting will update existing events instead of creating duplicates.

Serve calendar subscriptions

bir serve

Starts a web server on localhost:8080 with a search page for looking up an address and a subscription URL per property:

http://localhost:8080/calendar/<eiendomId>.ics

Copy that URL and paste it into your calendar app's "Subscribe to calendar" / "Add from web" dialog; the search page also has one-click links for Google Calendar, Outlook and Apple Calendar. Calendars cover one month back and twelve months ahead, and responses are cached for six hours.

Hosted calendars (Google, Outlook, iCloud) fetch the URL from their own servers, so a localhost URL only works with a desktop calendar app. To share a subscription, run bir serve on a public host behind HTTPS.

Behind a reverse proxy the links are built from X-Forwarded-Host and X-Forwarded-Proto, falling back to the Host header. If your proxy does not set those, the links end up pointing at the container's address — set --base-url https://bir.example.com (or BIR_BASE_URL) to pin them.

Notifications

Add ?alarm=<duration> to a subscription URL to embed a VALARM in every event, offset from midnight on the collection day:

https://bir.example.com/calendar/<eiendomId>.ics?alarm=-6h   # 18:00 the day before
https://bir.example.com/calendar/<eiendomId>.ics?alarm=7h    # 07:00 the same day

The search page has a dropdown that rewrites the links for you. Offsets are Go durations, bounded to ±48h.

Whether the alarm actually fires is up to the subscribing app: Apple Calendar honours it unless "Remove alerts" is checked on the subscription, while Google Calendar and Outlook ignore alarms in subscribed calendars. For those, set a notification on the calendar in their own settings instead.

Health checks

GET /up returns JSON for uptime checkers:

{"status":"ok","uptime":"3h2m1s","uptime_seconds":10921,"cached_calendars":2,"authenticated":true}

It only reports local state, so authenticated says a session exists, not that it still works. GET /up?check=bir runs a real search against BIR and returns 503 if that fails — useful for alerting, but do not poll it often.

Flags:

  • --addr — address to listen on (default localhost:8080)
  • --base-url — public base URL used in links, e.g. https://bir.example.com (also settable with the BIR_BASE_URL environment variable)
  • --months — months ahead to include (default 12)
  • --cache-ttl — how long generated calendars are cached (default 6h)

Docker

docker compose up -d

The compose file pulls src.timharek.no/tim/bir:latest; uncomment build: . to build from the checkout instead. Set BIR_BASE_URL to the public URL once the server is behind a reverse proxy.

Or run the image directly:

docker build -t bir .
docker run -p 8080:8080 bir

The image is scratch with the static binary, so bir serve listens on 0.0.0.0:8080 inside the container. Pass flags by overriding the command:

docker run -p 8080:8080 -e BIR_BASE_URL=https://bir.example.com bir

There is no shell in the image, so health checks must come from outside it — point your orchestrator at /up.

Building

mise run build

Contributing

Patches are welcome! Send them to ~timharek/public-inbox@lists.sr.ht.