Requires Home Assistant 2026.11.0b0 or later.
Local Bluetooth control of an xBloom Studio coffee machine from Home Assistant. Pour, grind, run saved recipes from an app-style sidebar dashboard, expose the brewer to Assist (LLM) — all without the cloud.
Built on the protocol work of two reverse-engineered BLE upstreams. The BLE client here is a clean-room native implementation (custom_components/xbloom/ble/, see ADR-001) — it does not import or copy either upstream. Both were formerly vendored under custom_components/xbloom/src/ as unmodified reference copies and have since been removed; they are credited by link only:
fhenwood/PyBloom(MIT) — the class-based client library whose connection/status/grinder/brewer/scale design informed the native client and coffee brew flow.brAzzi64/xbloom-ble(MIT) — HCI-snoop-confirmed protocol decode (PROTOCOL.md) that the tea recipe flow inbrewing.pydrew from.
This integration's own protocol reference now lives in docs/en/protocol.md, written from its own captures and APK decompilation. Huge thanks to Frederic, the PyBloom contributors, and Bruno Azzinnari for the protocol work that makes this integration possible.
The integration adds an xBloom page to the Home Assistant sidebar (coffee-maker icon), laid out like the official app and visible to all users:
- Brew — live weight ring, water temperature and flow, the selected recipe with its pour timeline, and load / start / pause / cancel.
- Recipes — a searchable coffee and tea library, recipe detail, an editor for dose, ratio, grind, RPM, bypass and every pour step, duplicate / delete, and writing to an Easy slot. The Cloud sub-tab searches xBloom's public recipe hub and imports a result, a share link or a recipe id into your recipes, no account needed.
- Manual — grinder, pour and scale controls.
- Settings — connection, PRO / Easy mode, Easy slots, pour radius, vibration, brightness, grinder calibration and device info.
It drives the same entities and services as everything else, so nothing it does is out of reach of automations.
| Brew | Manual |
|---|---|
- Sidebar dashboard — an app-style page for brewing, recipes, manual control and machine settings (see Dashboard).
- Local recipes are the source of truth — every recipe gets a stable local
uidand lives in HA; that's what the Recipe dropdown shows and brews. Seeded once at install (from your cloud account if linked, else xBloom's official public recipes), then no background sync — manage it from the HA UI, the recipe services, orconfiguration.yaml. - Cross-identifier addressing — every service/tool takes one
recipefield: local uid, cloud table id, share URL/id, or exact name. - Manual control — pour with custom temperature/volume/flow rate/pour pattern, grind with custom size/RPM, tare the scale, vibrate the tray.
- Per-brew overrides — brew a recipe with adjusted grind, RPM, dose, ratio, cup type, or bypass without editing it (dose/ratio rescales the pours proportionally); selecting a recipe also syncs the Grind Size / RPM sliders to it.
- Tea recipes (
cup_type: tea) — each steep is a pour withpausing= soak seconds; the firmware handles pour → soak → siphon-drain internally. - Easy Mode slot writing — push any recipe to onboard slot A/B/C via the slot buttons or the
write_recipe_to_easy_slotservice (auto-imports a share URL that isn't local yet); read-only sensor entities show what's stored in each slot. - Cloud as an import/export boundary — pull a shared recipe in (
cloud_import_recipe, no account needed), push a local one out for a share link (cloud_export_recipe), or browse xBloom's public community hub (cloud_search_collective_recipes). An account is optional, only needed for export. - Live telemetry — brewer temperature, scale weight, water-level state, current brew step.
- Event entities — errors (water shortage, no beans, abnormal dose/gear) and notifications (grinding/brewing/pour/bloom/pause/complete/tea soaking).
- LLM API — status, recipe CRUD, brewing, slot writing, import/export, and hub search exposed to voice/chat agents through the opt-in xBloom LLM API (see Assist / LLM tools) with safety confirmations (beans, dripper, filter, cup-on-scale, delete) — skipped entirely for no-grind recipes (e.g. a water-only pour).
- Korean and English UI translations.
- In HACS → Integrations → ⋮ → Custom repositories, add this repo URL with category Integration.
- Install xBloom Coffee Machine.
- Restart Home Assistant.
- Settings → Devices & Services → Add integration → search "xBloom".
- Enter the device's BLE MAC address (
xbloom scanfrom a terminal, or check xBloom Studio).
Copy custom_components/xbloom/ into your HA config's custom_components/ folder and restart.
The config flow handles MAC + telemetry interval + idle disconnect timeout. The Settings step (Settings → Devices & Services → xBloom → Configure → Settings) also sets the machine's own onboard display units — weight (g/oz/ml) and temperature (°C/°F) — pushed to the machine on every reconnect. This is separate from what HA itself shows for the weight/temperature sensors, which already follows your HA profile's unit system automatically.
On first install the recipe store is seeded once: a small bundled set is written immediately (so the dropdown is never empty), then a background task fetches your xBloom cloud account's recipes (if linked) or xBloom's official public recipes otherwise, skipping any name that already exists. A failed fetch (e.g. no internet at first boot) retries on the next restart. Linking an account later seeds its recipes once at that point. No background sync after that — the local store is yours.
Manage recipes from the HA UI (Settings → Devices & Services → xBloom → Configure → Add / Edit / Delete a recipe) or via the services below. Deleting is local-only and immediate; a copy on your cloud account is never touched.
Recipes can also be defined in configuration.yaml (lower priority — a UI/service recipe wins by name, and deleting a YAML recipe hides it):
xbloom:
recipes:
- name: Morning V60
cup_type: omni_dripper
grind_size: 35
dose_g: 18
ratio: 15.6
pours:
- volume_ml: 50
temperature_c: 93
flow_rate: 3.0
pause_seconds: 30
- volume_ml: 200
temperature_c: 92
flow_rate: 3.0
pause_seconds: 0
- name: Sencha Tea
cup_type: tea
pours:
- volume_ml: 150
temperature_c: 80
pause_seconds: 60
- volume_ml: 150
temperature_c: 80
pause_seconds: 90For tea recipes, each pour represents one steep cycle: volume_ml is the steep water, temperature_c is the water temperature, pause_seconds is the soak time in seconds. The machine handles the siphon drain automatically.
The LLM tools are opt-in per conversation agent: in your agent's settings (Settings → Voice assistants → your assistant → conversation agent options) enable the "xBloom Coffee Machine (MAC)" API under the LLM API selection. The tools never ride along in the plain Assist API — an agent without the xBloom API selected sees none of them, and the tool code isn't even loaded until the API is first used. One API is registered per machine, so multi-machine households pick per agent.
With the MCP Server integration
set up, the same tools are also served over MCP at
/api/mcp/xbloom_coffee_<entry_id> (admin access token required).
A saved recipe can be run with adjustments for a single brew without editing it. Choosing a recipe in the Recipe select syncs the Grind Size / Grinder RPM sliders to its values; whatever they hold at brew time is used. Tea and no-grind recipes ignore grind/RPM.
Every top-level scalar can be overridden per brew: grind_size, rpm, dose_g, ratio, cup_type, bypass_volume, bypass_temperature. A dose_g/ratio override rescales the pour volumes proportionally (sum of pours + bypass = dose × ratio). Bypass can be added even to recipes that have none; tea recipes never bypass or grind.
These overrides are available via the xbloom.execute_recipe service:
service: xbloom.execute_recipe
target:
device_id: <your xbloom device> # optional if you only have one machine
data:
recipe: Morning V60 # uid / cloud id / share URL / name — optional, defaults to the selected recipe
grind_size: 42
rpm: 90
dose_g: 20 # pours rescale to keep total water = dose × ratio
bypass_volume: 50
bypass_temperature: 92Through Assist (LLM), xbloom__get_recipe returns a recipe's full detail (grind, RPM, bypass, and each pour's volume / flow rate / pattern) and xbloom__execute_recipe accepts the same scalar overrides plus per-pour pour_overrides (volume / flow rate / pattern keyed by 0-based pour_index), so an agent can tune individual pours on request.
Nine services cover the whole recipe surface (Developer Tools → Actions). Wherever a
service takes a recipe, it accepts the recipe's local uid, cloud table id,
share URL/id, or exact name — list_recipes returns the uids.
| Service | Does |
|---|---|
list_recipes |
List every local recipe (uid, source, cup type, dose, grind, pour count, and any cloud id / share URL), optionally filtered by a name query. |
create_recipe |
Create a new local recipe from inline recipe_yaml. Returns its uid. Nothing is uploaded. |
edit_recipe |
Change one or more fields of a local recipe; omitted fields keep their values. Pointing it at a share URL that isn't local yet imports a copy first and edits that. |
delete_recipe |
Delete a local recipe — the dropdown updates immediately. A copy on your cloud account is not touched (cloud deletion happens in the official app). |
execute_recipe |
Brew a recipe, with optional per-brew scalar overrides (see above). |
write_recipe_to_easy_slot |
Store a recipe on onboard Easy Mode slot A/B/C. A share URL that isn't local yet is auto-imported first. |
cloud_import_recipe |
Fetch a recipe from a share-h5.xbloom.com link, a collective.xbloom.com/recipe/{id} community-hub link, or a share id, and save it locally (with a new uid). No account needed. |
cloud_export_recipe |
Push a local recipe to your xBloom cloud account and return its cloud id, share link, and the recipe. Re-exporting the same recipe updates the same cloud entry (the link stays stable). Without an account configured, nothing is uploaded and only the recipe is returned. |
cloud_search_collective_recipes |
Search xBloom's public community recipe hub (collective.xbloom.com) — no account needed. Filter by keyword, coffee/tea, official/user, and multi-select machine / cup type / origin / varietal / process / roast / flavor facets, with sorting. |
service: xbloom.cloud_import_recipe
data:
share_url: "https://share-h5.xbloom.com/?id=KmMzhYCe5itq%2FJcqOLhiag%3D%3D"The collective-search facet dropdowns are a snapshot of the hub's category codes. If xBloom adds a category the dropdown doesn't know yet, type its numeric code directly (the fields accept custom values) — and please open an issue with the code so it can be added to the snapshot and translated in the next release.
Cloud account (optional) — only cloud_export_recipe needs one. Enter your
xBloom app email/password in the config flow's "xBloom Cloud Account" step during
initial setup, or add/update/remove it later via Settings → Devices & Services →
xBloom → Configure → Cloud account. Signed in with Apple and have no xBloom
password? Use xBloom's own "forgot password" flow (with the email Apple relays,
visible in the app's account settings) to set one first.
Through Assist (LLM), the same surface is exposed as tools: xbloom__list_recipes,
xbloom__get_recipe, xbloom__create_recipe, xbloom__edit_recipe,
xbloom__delete_recipe (asks for explicit confirmation before deleting),
xbloom__execute_recipe, xbloom__write_easy_slot, xbloom__import_cloud_recipe,
xbloom__export_recipe, and xbloom__search_collective_recipes.
- Removed services:
cloud_search_recipes(→list_recipes, now local),cloud_create_recipe(→create_recipelocally +cloud_export_recipeto upload),cloud_edit_recipe(→edit_recipe, local semantics), andcloud_delete_recipe— deleting a recipe from your cloud account is no longer possible from HA; do it in the official app.delete_recipeonly removes the local copy. - Removed fields:
recipe_name,table_id, andrecipe_idare gone — every service now takes the single cross-identifierrecipefield. - No more background sync: the hourly cloud recipe sync is replaced by a one-time seed at install (and once more when an account is linked later). Recipes you delete stay deleted; recipes you edit are yours, with no synced original behind them.
| Brew method | Range |
|---|---|
| Turkish | 0–3 |
| Espresso | 0–18 |
| Moka Pot | 17–44 |
| Filter Coffee Machine | 12–66 |
| Aeropress | 13–71 |
| Siphon | 18–57 |
| V60 | 21–47 |
| Pour Over | 22–68 |
| Steep-and-release | 25–59 |
| Cupping | 26–61 |
| French Press | 47–80 |
| Cold Brew | 58–80 |
| Cold Drip | 59–80 |
- xBloom Original is not supported: this integration only talks to xBloom Studio over Bluetooth LE (see
manifest.json'sbluetoothmatcher) — Original uses an entirely different Wi-Fi protocol, and the maintainer has no Original unit to test. The cloud API also hardcodesadaptedModel: 1(Studio), so the account recipe seed andcloud_export_recipeare unverified for an Original-only account. - MachineInfo on some firmwares: certain firmware revisions never push
RD_MachineInfo, so the Model / Serial / Firmware sensors may stayunknown. The water-level binary sensor falls back to event-driven detection on those firmwares. - Manual cup detection: the scale auto-tares any weight present at power-on, so a cup placed before boot reads as 0 g — when this happens the LLM
xbloom__execute_recipetool adds the cup to the checklist it returns when it arms the recipe. - Weight reads unknown while the machine sleeps or is disconnected: asleep, the machine reports a fixed 50 g with nothing on the scale, so the weight sensor shows unknown instead of a made-up reading. The same applies while the Bluetooth link is down (idle standby, out of range). Wake the machine (a button press or a pod insert) to get a live reading.
- Recipe water source: the manual pour entity respects the water-source select (tank vs. direct plumbed); recipe execution does not — the firmware runs its own pour sequence internally.
- Tea soak timing is approximately calibrated: the wait between steeps is scaled from the recipe's
pausingseconds using a factor derived from a couple of stopwatch measurements — seebrewing-notes.mdfor details if you need tighter timing.
A devcontainer is provided for testing the integration against a real Home Assistant install. Its base image is the official HA image pinned to 2026.11.0b0 (in .devcontainer/devcontainer.json, matching hacs.json's floor), so HA core and every runtime dependency come baked in — scripts/setup only installs dev tools. Open the folder in VS Code with the Dev Containers extension and run:
scripts/developHA binds the standard port 8123 inside the container. The container's hostname is set to ha-xbloom-dev so it's distinguishable from any production HA instance you run on the host network. VS Code forwards 8123 to the host (and auto-picks a different host port if 8123 is already taken there).
MIT. This integration draws on the protocol work of two reverse-engineered upstreams, both MIT-licensed and both credited by link (they were formerly vendored in-tree and have since been removed — see ADR-001):
fhenwood/PyBloom— MIT.brAzzi64/xbloom-ble— MIT, © Bruno Azzinnari.
No upstream code is copied into this repository's own clean-room source; the native BLE client was built independently (see ADR-001). The MIT terms of both upstreams are satisfied by attribution to their repositories, where each project's own LICENSE lives.