{"id":51423074,"url":"https://github.com/waevans10/crema","last_synced_at":"2026-07-05T01:02:04.751Z","repository":{"id":369358983,"uuid":"1288734289","full_name":"waevans10/crema","owner":"waevans10","description":"AI shot reviews for GaggiMate — self-hosted on a Raspberry Pi, Claude-powered, you approve every change ☕","archived":false,"fork":false,"pushed_at":"2026-07-04T23:04:28.000Z","size":906,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-04T23:07:16.871Z","etag":null,"topics":["ai","anthropic","claude","coffee","espresso","gaggimate","home-automation","raspberry-pi","self-hosted"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/waevans10.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"buy_me_a_coffee":"waevans10f"}},"created_at":"2026-07-03T23:04:34.000Z","updated_at":"2026-07-04T23:04:32.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/waevans10/crema","commit_stats":null,"previous_names":["waevans10/crema"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/waevans10/crema","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/waevans10%2Fcrema","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/waevans10%2Fcrema/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/waevans10%2Fcrema/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/waevans10%2Fcrema/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/waevans10","download_url":"https://codeload.github.com/waevans10/crema/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/waevans10%2Fcrema/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35140189,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-04T02:00:05.987Z","response_time":113,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","anthropic","claude","coffee","espresso","gaggimate","home-automation","raspberry-pi","self-hosted"],"created_at":"2026-07-05T01:02:02.632Z","updated_at":"2026-07-05T01:02:04.740Z","avatar_url":"https://github.com/waevans10.png","language":"Python","funding_links":["https://buymeacoffee.com/waevans10f","https://www.buymeacoffee.com/waevans10f"],"categories":[],"sub_categories":[],"readme":"# crema ☕\n\n**Automated espresso shot reviewer for smart machines.** crema pulls your recent\nshots off the machine — **GaggiMate** fully supported, **Gaggiuino** in beta —\nsends their telemetry to Claude, and gets back concrete grind / dose / profile\nsuggestions, grounded in *your* grinder, *your* beans, and *your* tasting notes.\nIt runs unattended on an always-on box (a Raspberry Pi is ideal) on the same\nnetwork as the machine, and can opt in to a community shot pool that's building\nthe first open advice→outcome espresso dataset.\n\nThink of it as the hands-off counterpart to the interactive\n[`gaggimate-mcp`](https://github.com/julianleopold/gaggimate-mcp) server: instead\nof opening a chat to ask Claude about your shots, crema watches for new shots and\nreviews them automatically, then shows the result in a web page with a one-click\npath to draft and push a corrected profile.\n\n## Screenshots\n\nThe report: a scored review of the latest shot, with grind / dose / profile\nsuggestions (grind advice phrased for *your* grinder):\n\n![crema web report — scored review with suggestions](docs/report.png)\n\nProfile drafts wait for your approval — refine them with taste notes, and any\nchange to the shot's stop conditions must be explicitly acknowledged:\n\n![crema profile drafts — approve, discard, or refine with notes](docs/report-edits.png)\n\n## What you need\n\n- A **GaggiMate**-controlled espresso machine, reachable on your home network\n  (its API is LAN-only — see [How it works](#how-it-works)) — or a\n  **Gaggiuino**-modded machine ([beta support](#gaggiuino-support-beta):\n  reviews/notes/sharing work; profile push-back is GaggiMate-only).\n- A separate **always-on computer on the same network** to run crema — a\n  Raspberry Pi (3 / 4 / 5, or Zero 2 W) is ideal, but any always-on Linux or\n  macOS machine works. **This is _not_ the espresso machine's controller:** crema\n  needs Python and a real operating system, so it can't run on the GaggiMate's\n  ESP32 board or an Arduino — it runs on its own little computer alongside the\n  machine.\n- **Python 3.13+** on that computer.\n\n### Minimum hardware \u0026 OS support\n\ncrema is deliberately light — a plain Python web app with SQLite, no JavaScript\nframeworks, and the AI heavy lifting done by Anthropic's servers, not your box.\n\n| | Minimum that works | Notes |\n|---|---|---|\n| **Device** | Raspberry Pi Zero 2 W / Pi 3 or newer | Runs happily on a 32-bit armv7 Pi (that's what it was built on); any x86/ARM mini PC, old laptop, or NAS that runs Linux is more than enough |\n| **RAM** | ~512 MB total system RAM | crema itself uses on the order of 100 MB |\n| **Disk** | ~300 MB | Python 3.13 + dependencies; the shot database itself is tiny (KBs per shot, pruned after 30 days by default) |\n| **CPU / GPU** | Anything | It idles between reviews; no GPU, no local AI model |\n\n| OS | Status |\n|---|---|\n| **Linux** (Raspberry Pi OS, Debian, Ubuntu, …) | ✅ Fully supported — the intended home; `deploy/setup.sh` + systemd give you the unattended setup |\n| **macOS** | ✅ Works for running crema manually (`crema serve`, reviews, pushes); the systemd deploy script is Linux-only, so schedule with cron or a LaunchAgent instead |\n| **Windows** | ⚠️ Untested. The Python app itself should run, but the setup script, systemd units, and parts of `crema doctor` assume a Unix system — use **WSL** (Windows Subsystem for Linux) and follow the Linux instructions |\n- A **paid Anthropic API account** for Claude — this is what does the reviewing\n  (see [The AI (Claude)](#the-ai-claude)). **This costs real money: you pay Anthropic\n  directly, per shot reviewed.** It's cheap for home use (roughly a dollar or two\n  a month), but it is a metered bill, not a free service — read [Cost](#cost)\n  before you set this up.\n\nYou do **not** need the machine powered on to browse past reviews or draft a\nprofile edit — only to pull new shots or push an approved edit back.\n\n## What it does\n\n- **Ingest** — downloads new shots (binary `.slog`) from the device and parses\n  them into AI-friendly JSON with physics diagnostics (channeling risk,\n  puck resistance, temp stability, profile compliance).\n- **Review** — hands the recent-shot window to Claude and stores a structured\n  set of suggestions plus a 1–10 quality score.\n- **Draft** — turns a review's profile suggestions into a complete, validated\n  profile (rewritten from the one the shot ran on), clamped to device-safe\n  bounds, stored as a *pending edit*. You can add your own notes (how it tasted,\n  what you want) when drafting, and **refine** any draft with further notes\n  before approving — Claude redrafts and the old version is superseded.\n- **Approve \u0026 push** — on your say-so, writes the edit to the machine as a **new\n  `[AI]` profile** over WebSocket (never overwrites your original; you select it\n  on the machine). If a draft changes the shot's **stop conditions** (volume /\n  flow / pressure targets that end the shot), crema lists exactly what changed\n  and requires an explicit acknowledgement before it will push.\n- **Report** — a small web page shows reviews, drafts, and recent shots, with\n  “Run review”, “Draft profile edit”, and “Approve \u0026 push” buttons.\n- **Taste feedback loop** — record how each shot tasted (an espresso taste\n  guide with a spectrum graphic, standard-vocabulary chips, and per-shot\n  telemetry hints helps newer palates find the words), tag each shot with its\n  beans, and the next review reconciles your palate with the curves. Reviews\n  also see their own earlier advice alongside the shots that followed, so they\n  build on what worked instead of repeating what didn't.\n- **Community shot pool (opt-in)** — say yes once (at setup, in the web UI, or\n  `crema autoshare on`) and crema shares your anonymized shot data after each\n  review, growing the open dataset. See\n  [Sharing shot data](#sharing-shot-data-opt-in).\n\nGrind and dose/yield are suggested as text (manual bench changes); only profile\nchanges get drafted and pushed.\n\n\u003e [!NOTE]\n\u003e **crema complements GaggiMate — it doesn't replace it.** You still brew from\n\u003e the machine and use GaggiMate's own display and web UI for live shot graphs,\n\u003e pressure/flow curves, and profile management. crema sits alongside, reading\n\u003e the same shot history and adding an AI review layer on top: scores,\n\u003e diagnoses, and suggested adjustments you approve back into the machine.\n\n## How it works\n\nThe GaggiMate machine only exposes its API on your **home network** (list/\ndownload shots over HTTP, read/write profiles over WebSocket). So a scheduled\nreviewer needs an always-on box on that same network — the Pi that's already\nrunning is exactly that. Everything runs there and reaches the machine directly;\nno cloud, no tunnel-to-device.\n\n```\n        always-on box (e.g. a Pi) on your LAN\n ┌────────────┐   ┌─────────────────────────────────────────┐\n │  GaggiMate │   │  timer / \"Run review\" button            │\n │  machine   │◀──│    │                                    │\n │ HTTP + WS  │   │    ├─ pull new shots → parse .slog       │\n └────────────┘   │    ├─ send recent shots → Claude review  │\n   direct LAN     │    ├─ store shots + reviews (SQLite)     │\n   access         │    └─ web report ── \"Approve \u0026 push\" ────┼──▶ new [AI]\n                  └─────────────────────────────────────────┘    profile on\n                                                                  the machine\n```\n\nTo spend as little as possible, Claude is only called when there's a **new shot\nto review** — the timer ingests on a schedule but skips the review unless\nsomething new arrived. See [Cost](#cost).\n\n## How crema is different\n\nThe closest existing projects are **interactive**: the\n[`gaggimate-mcp`](https://github.com/julianleopold/gaggimate-mcp) server lets you\nchat with an LLM about your shots, and hosted services generate profiles from a\nbean description. crema fills a different niche — **unattended and self-hosted**:\n\n- It runs on a timer and reviews new shots on its own; you don't open a chat.\n- Everything lives on your own box; no shot data leaves your network except the\n  telemetry sent to Claude for the review itself.\n- It's cost-gated by design (a review only happens on a genuinely new shot).\n- Profile changes are always **suggest → draft → you approve → push**, and a\n  push creates a new `[AI]` profile, so your originals are never touched.\n\n## Quickstart\n\nRun these on your always-on box (the Pi or a PC/Mac) — **not** on the espresso\nmachine. This gets crema working by hand; to run it 24/7, see\n[Running it unattended on a Pi](#running-it-unattended-on-a-pi).\n\n**1. Get the code.**\n\n```bash\ngit clone https://github.com/waevans10/crema.git\ncd crema\n```\n\n**2. Install [uv](https://docs.astral.sh/uv/)** — it manages Python 3.13 and the\ndependencies for you. Skip if you already have it:\n\n```bash\ncurl -LsSf https://astral.sh/uv/install.sh | sh\n```\n\n(On a Raspberry Pi, `uv` also installs Python 3.13 for you — Pi OS ships 3.11.\nPrefer your own Python 3.13? Use `pip install -e .` and drop the `uv run` prefix\nfrom the commands below.)\n\n**3. Configure.**\n\n```bash\ncp .env.example .env\nnano .env          # set ANTHROPIC_API_KEY and GAGGIMATE_GAGGIMATE_HOST\n```\n\nFor `GAGGIMATE_GAGGIMATE_HOST`, use the machine's address. **Recommended:** give\nit a fixed IP with a DHCP reservation on your router — a plain DHCP address\nchanges on reconnect and crema will lose the machine. `gaggimate.local` works on\nmany networks but not all, and never across subnets.\n\n**4. Install dependencies and check the connections.**\n\n```bash\nuv sync                    # creates .venv with the `crema` command\nuv run crema doctor        # verifies it can reach the machine + Claude\n```\n\n**5. Pull shots, review them, and open the report.**\n\n```bash\nuv run crema ingest        # pull new shots off the machine\nuv run crema review        # send recent shots to Claude for a scored review\nuv run crema serve         # web report at http://127.0.0.1:8765\n```\n\nOpen `http://127.0.0.1:8765` and click **Run review** to review on demand.\n\n### All commands\n\nRun each as `uv run crema \u003ccommand\u003e` (or activate the venv once with\n`source .venv/bin/activate`, then just `crema \u003ccommand\u003e`):\n\n```bash\ncrema doctor              # check device + Claude connectivity\ncrema ingest              # pull new shots (also prunes shots past the retention window)\ncrema review [--force]    # ingest + review (auto-gated: only new shots, only if autoreview on)\ncrema analyze SHOT_ID     # review one specific shot\ncrema draft [REVIEW_ID]   # draft a profile edit (--profile-id to target a specific profile)\ncrema edits               # list drafted / pushed edits\ncrema push EDIT_ID        # approve \u0026 push an edit to the machine as a new [AI] profile\ncrema discard EDIT_ID     # discard a drafted edit\ncrema autoreview [on|off] # toggle automatic review of new shots by the timer\ncrema grinder [\"DESC\"]    # describe your grinder so grind advice uses its steps/clicks\ncrema coffee [\"DESC\"]     # describe the beans in the hopper so advice fits them\ncrema taste SHOT_ID \"...\" # record how a shot tasted (--beans to set that shot's coffee)\ncrema serve               # web report at http://127.0.0.1:8765\ncrema export              # write your anonymized shot bundle to a JSON file\ncrema share               # one-off share to the community pool (shows terms, asks)\ncrema autoshare [on|off]  # opt in/out of automatic sharing after each review\n```\n\n## Running it unattended on a Pi\n\nAfter steps 1–4 above, one script installs everything so crema reviews on a timer\nand keeps the web report always up:\n\n```bash\nuv run crema doctor        # confirm it works first\nbash deploy/setup.sh       # installs systemd services: 15-min review timer + web report\n```\n\n`deploy/setup.sh` binds the web report to your LAN, generates a password for it,\nand starts everything on boot (it asks for `sudo` where it needs it — run it as\nyour normal user, not with `sudo`). For the full walkthrough, including how to\nview the report from your laptop over SSH, see\n[`deploy/PI_SETUP.md`](deploy/PI_SETUP.md).\n\n## The AI (Claude)\n\ncrema uses **Claude** (via the Anthropic SDK) to review shots and draft profiles.\nTwo things it relies on:\n\n- **Structured output** — reviews and drafts come back as validated JSON via\n  Claude's `messages.parse`, so the app can render and act on them safely.\n- **Reasoning on the physics** — diagnosing an extraction from\n  temperature/pressure/flow curves is exactly what these models are good at.\n\nTwo models are used, both set in `.env`: routine reviews run on the cheaper\n`CREMA_REVIEW_MODEL` (default `claude-sonnet-5`), and the occasional profile draft\nuses the stronger `CREMA_DRAFT_MODEL` (default `claude-opus-4-8`). Point either at\na different Claude model if you like — e.g. set both to `claude-sonnet-5` to keep\ncosts down.\n\n### Why an LLM and not a trained model?\n\nA fair question — \"why not train a model on the shot data?\" comes up, so here's\nthe reasoning.\n\nA model trained from scratch on one machine's shots would be **siloed**: it\nlearns *your* machine's quirks entangled with *your* beans and *your* palate,\nand can't tell them apart from general extraction physics. Change the bean, the\ngrinder, or the user, and it's confidently wrong. Making it generalize would\nneed coverage of the whole space — many machines × grinders × beans × palates —\nand home espresso produces a few shots a day. That dataset never happens at\nhobby scale.\n\nAn LLM with **no** context has the opposite problem: broad extraction knowledge,\nzero idea what's in your portafilter — generic advice.\n\ncrema takes the hybrid: the LLM supplies the breadth (it has effectively already\nabsorbed the collective dial-in experience of the espresso world), and the\ncontext supplies the depth. Each review is **grounded** in:\n\n- the full telemetry of your recent shots (pressure/flow curves, puck\n  resistance, channeling risk, temperature stability), newest first\n- **your grinder**, so grind advice comes back in its own steps/clicks\n- **your coffee** (roast level, roast date), so a light Ethiopian and a dark\n  blend get different advice — new shots are stamped with the beans in the\n  hopper at ingest, and each shot's beans can be edited individually, so a\n  bean change mid-window stays accurate\n- **your tasting notes** per shot — telemetry can't taste sourness; you can\n- **its own previous advice**, interleaved with the shots that followed — so it\n  can see whether \"2 steps finer\" worked, build on what did, and change strategy\n  on what didn't instead of repeating it\n\nThat last one matters: the advice→outcome loop is what training would have\nbought, obtained instead by showing the model its own track record. You get\nspecificity *and* generality; a from-scratch model forces you to pick one.\n\n(Where a trained model *does* win — millions of labeled examples, a fixed\ndistribution, a numeric output — is the opposite of this regime: tiny data,\nhuge variance across setups, and advice as the output.)\n\n## Configuration\n\nAll via `.env` (see [`.env.example`](.env.example)):\n\n| Variable | Purpose | Default |\n|---|---|---|\n| `ANTHROPIC_API_KEY` | Claude API key (read by the SDK) | — |\n| `GAGGIMATE_GAGGIMATE_HOST` | machine hostname/IP on the LAN | `gaggimate.local` |\n| `CREMA_REVIEW_MODEL` | model for routine reviews | `claude-sonnet-5` |\n| `CREMA_DRAFT_MODEL` | model for the deeper profile-drafting step | `claude-opus-4-8` |\n| `CREMA_REVIEW_WINDOW` | shots per review | `5` |\n| `CREMA_RETENTION_DAYS` | prune shots older than this (0 = keep all) | `30` |\n| `CREMA_AUTOREVIEW` | default for timer auto-review (UI toggle overrides) | `false` |\n| `CREMA_DISCORD_WEBHOOK_URL` | Discord webhook for shot score notifications | — |\n| `CREMA_WEB_USER` / `CREMA_WEB_PASSWORD` | web login (blank pw = no login; password managers supported) | `crema` / — |\n| `CREMA_DB_PATH` | SQLite path | `./crema.db` |\n| `CREMA_HOST` / `CREMA_PORT` | web bind | `127.0.0.1` / `8765` |\n\nThe web UI binds to loopback by default. To reach it off-network, put a\nCloudflare Tunnel (or similar) in front of *just the UI* — don't bind `0.0.0.0`\nwithout a password.\n\n## Cost\n\n\u003e [!IMPORTANT]\n\u003e **crema uses the paid Anthropic API. Running it puts real charges on your own\n\u003e Anthropic account** — you set up billing with Anthropic and pay them directly\n\u003e for every review. There is no free tier that covers this, and crema doesn't\n\u003e bundle any credits. It's inexpensive for normal home use, but you should\n\u003e understand that reviewing shots costs money before you start.\n\nThe good news is that it's designed to stay cheap. crema only calls Claude when\nthere's **a new shot to review**: the scheduled timer ingests every 15 min but\n**skips** the review unless a new shot came in, and the web “Run review” button\ndoes the same — so you pay per shot you actually pull, not per timer tick. A\nsingle review is roughly **a few cents**, and `crema review` prints the token\ncount each time so you can watch what you're spending.\n\n**Ballpark:** a handful of shots a day lands around **a dollar or two a month**.\nHeavy use (many shots a day, a large review window, the Opus model on every\nreview) costs more — it scales with how much you review.\n\n**Nothing spends automatically until you opt in.** Auto-review is **off** by\ndefault (`CREMA_AUTOREVIEW=false`), so the timer won't call the API on its own —\nreviews only happen when you press “Run review” (or run `crema review`) until you\ndeliberately turn auto-review on. Levers if you want it cheaper: lower\n`CREMA_REVIEW_WINDOW` (fewer shots per review = fewer input tokens), or keep\n`CREMA_REVIEW_MODEL=claude-sonnet-5` (the cheap default) rather than an Opus\nmodel. You can also set a spend limit on your key in the\n[Anthropic Console](https://console.anthropic.com/) as a hard backstop.\n\n## Gaggiuino support (beta)\n\ncrema can also ingest from a [Gaggiuino](https://gaggiuino.github.io/)-modded\nmachine — set two lines in `.env`:\n\n```\nCREMA_MACHINE=gaggiuino\nCREMA_GAGGIUINO_URL=http://gaggiuino.local   # or its reserved IP\n```\n\nShots are pulled from Gaggiuino's REST API and normalized (pressure/flow/\nweight/temperature curves, profile phases). Reviews, tasting notes, beans, and\nthe community pool all work identically; **profile drafting/push-back is\nGaggiMate-only for now** (Gaggiuino's profile-write API isn't wired up yet —\nPRs welcome). Untested against real hardware so far: built from the API's\npublished shapes, so reports from actual Gaggiuino machines are gold.\n\n## Sharing shot data (opt-in)\n\nEvery crema install quietly builds the dataset a trained espresso model would\nneed — (context → advice → next shot → outcome) examples. If enough people pool\ntheirs, that dataset exists for the first time. Strictly opt-in:\n\n**How you opt in** (nothing is ever shared before you do):\n\n- **At install** — `deploy/setup.sh` asks once, shows the terms, and takes\n  yes or no.\n- **Any time after** — `crema autoshare on` (CLI) or the \"Opt in to the\n  community shot pool\" control in the web report; both show the terms first.\n\nOnce opted in, a fresh anonymized snapshot uploads automatically after each\nreview — no further prompts — until you turn it off (`crema autoshare off`, or\nthe web toggle). If the terms ever change, auto-share pauses until you\nre-accept. Every uploaded bundle records which terms version you accepted and\nwhen.\n\nThe manual tools remain:\n\n- `crema export` writes your anonymized bundle to a local JSON file — shots,\n  telemetry, beans, tasting notes, and the reviews' advice, identified only by\n  a random install UUID. Read it: it is exactly what sharing sends.\n- `crema share` does a one-off share with the terms shown and a confirmation\n  prompt — useful if you'd rather not enable auto-share.\n\nThe pool endpoint is discovered from a pointer file in this repo (`.pool-url`),\nso sharing needs zero setup and the endpoint can move without breaking old\ninstalls; set `CREMA_SHARE_URL` to use a self-hosted pool, or to `off` to\ndisable sharing entirely.\n\nPlain-words terms: free-text fields (profile names, coffee, tasting notes) are\nincluded as you typed them, so read your export first. By sharing you grant the\ncrema project a license to use the data **including commercially**; the pooled\ndataset is published for community use under **CC BY-NC 4.0**\n(non-commercial, attribution).\n\n**Withdrawal:** open a GitHub issue quoting your install id (shown by\n`crema export`) and your raw submissions are deleted from the pool. Data\nalready included in a published dataset release stays licensed as released —\nthat's the honest trade-off of a public dataset.\n\nThe collection endpoint is a small Cloudflare Worker — see\n[`share-worker/`](share-worker/) to run your own. Shot telemetry is your own\nmachine's measurement data; the pool never includes GaggiMate's shipped\nprofiles or any of its (CC BY-NC-SA-licensed) code.\n\n## Scheduling with cron (alternative)\n\n[Running it unattended on a Pi](#running-it-unattended-on-a-pi) uses systemd\n(installed by `deploy/setup.sh`). If you'd rather use cron instead, run a review\nevery 15 minutes with:\n\n```\n*/15 * * * * cd /home/pi/crema \u0026\u0026 /home/pi/crema/.venv/bin/crema review \u003e\u003e crema.log 2\u003e\u00261\n```\n\n## Reuse\n\nThe binary parsing and device API clients are vendored from the MIT-licensed\n[`gaggimate-mcp`](https://github.com/julianleopold/gaggimate-mcp) project under\n`src/gaggimate_mcp/` (see its `_vendor_meta/`), so crema doesn't reimplement the\n`.slog` format.\n\n## Contributing\n\nIssues and pull requests are welcome — see [`CONTRIBUTING.md`](CONTRIBUTING.md)\nfor how to run the tests and the layout of the code.\n\n## Support ☕\n\ncrema is free and open source. If it saves you a few bad shots and you'd like to\nchip in toward the Claude API costs, you can\n[buy me a coffee](https://www.buymeacoffee.com/waevans10f) — entirely optional,\nand genuinely just to cover costs. There's nothing behind a paywall.\n\n## License \u0026 credits\n\ncrema is released under the [MIT License](./LICENSE).\n\nCredits:\n- [`gaggimate-mcp`](https://github.com/julianleopold/gaggimate-mcp) by\n  julianleopold (MIT) — vendored for the `.slog` parser, device HTTP/WebSocket\n  clients, and shot transformer.\n- [GaggiMate](https://gaggimate.eu/) by jniebuhr — the open-source smart-controller\n  project this works with. “GaggiMate” and “Gaggia” are used descriptively; crema\n  is an independent project and is not affiliated with or endorsed by either.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwaevans10%2Fcrema","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwaevans10%2Fcrema","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwaevans10%2Fcrema/lists"}