Home Assistant integration for WiFi-connected JURA coffee machines (S8, EB,
TT237W series). Built on the reverse-engineered
jura-connect library — talks
directly to the machine's WiFi dongle on TCP/51515. No cloud, no vendor
account.
Requires jura_connect>=0.14.0. The integration ships the Python dependency
declaration; Nix users get it pinned via the flake input.
- Auto-discovery on the local network (UDP broadcast first, with a TCP /24 fallback for firmwares like TT237W that don't reply to UDP); manual IP entry is always available.
- One-shot pairing — press OK on the machine once, the integration persists the resulting auth-hash on the config entry and reconnects silently afterwards.
- Per-machine profiles — pick your model from a dropdown of 89 known JURA variants (S8 EB, ENA 8, Z8, …). The profile drives the names of alerts, brew counters, and machine settings. Auto-detected from the UDP broadcast's article number when available.
- Status — overall machine state derived from the active-alert bits.
- Brew
<recipe>— one sensor per recipe the machine reports (espresso, coffee, cappuccino, macchiato, americano, …). Set is discovered from the machine; names from the profile when configured. - Brew total — lifetime brews across all recipes.
- Service
<kind>level —cleaning,descale,filter change— the percent-to-next-service indicators (0–100 %). - Setting
<name>— current value of each machine setting (language, hardness, auto-off, units, …); item-driven settings surface as the friendly catalogue name, sliders as the integer value.
- Alert
<name>— one entity per status bit (fill water,no beans,empty grounds, drip tray, milk warning, heating up, coffee ready, …). Problem-class alerts live in the main view; running and informational bits move to the diagnostic section.
select.*entities for switch / combobox / item-slider settings (language, units, auto-off delay, milk rinsing, frother instructions, …). Writes are validated against the profile before any TCP session opens, so invalid values surface a clear "klingon is not a recognised value. Allowed: german=01, english=02, …" message rather than a backend error.number.*entities for step-slider settings (currently water hardness on EF1091) with the min/max/step pulled from the profile XML.
- Machine type — the EF code + friendly name (e.g.
S8 (EB)withmachine_type=EF1091on attributes). - Connectivity — the canonical "is the machine reachable right now"
signal (
binary_sensorwithdevice_class=connectivity). Wire automations against this rather than the per-entityavailableflag. - Cycles
<kind>— the six maintenance counters (cleaning, descale, filter change, cappu rinse, coffee rinse, cappu clean). - Running / informational binary sensors (heating up, coffee ready, welcome, please wait, …).
Entities keep showing their last value when the machine is offline — JURA dongles sleep regularly and v0.1 caused dashboards to flash with every failed poll. The connectivity binary sensor is the single source of truth for reachability.
Entity names use Home Assistant's native per-user translations, so each
client sees them in its own UI language. English ships in strings.json
(mirrored to translations/en.json); German (de) is fully translated in
translations/de.json. The dynamic Brew <recipe> and Setting <name>
entities translate their prefix and keep the machine-supplied name as a
placeholder. To add a language, copy translations/de.json to
translations/<lang>.json and translate the name values.
The repo ships a companion dashboard card (www/jura-brew-card.js):
status pill, Product picker, Strength / Water / Temperature sliders and
a Brew button in one card. Copy the file to your HA config/www/
directory and register it as a module dashboard resource pointing at
/local/jura-brew-card.js. HA serves /local/* with a 31-day
Cache-Control, so append a version query (?v=<md5>) and bump it on
every card update, or browsers keep serving the stale copy.
type: custom:jura-brew-card
machine: kaffeebert # optional — see below- Zero-config: with no options the card auto-discovers the brew + status entities; with several machines it picks the first (alphabetically) and logs a warning.
machine:pinning matches by slug token, not exact prefix:machine: kaffeebertresolves a machine whose entities drifted apart in the entity registry (e.g. brew entities underselect.kuche_kaffeebert_brew_*while connectivity keptbinary_sensor.kaffeebert_connectivity). A pinned machine never borrows another machine's entities; exact per-entity options (product:,connectivity:, …) always win over auto-resolution.- Offline handling: the
binary_sensor.<machine>_connectivityentity is the reachability signal. When it isoff(or the status sensor isunavailable), the card shows a grey Offline pill plus a "brewing unavailable" note, and disables the sliders and the Brew button — a retainedlast_presstimestamp on the brew button is not treated as "online".
| Service | Description |
|---|---|
jura.force_update |
Poll the machine immediately |
jura.lock_screen |
Lock the front-panel display |
jura.unlock_screen |
Unlock the front-panel display |
jura.brew |
Brew a product by name, or a raw recipe payload |
jura.clean |
Start coffee-system cleaning cycle (~5 min) |
jura.descale |
Start descaling cycle (30+ min, requires descaler) |
jura.filter_change |
Run water-filter change procedure |
jura.cappu_rinse |
Rinse the milk system |
jura.cappu_clean |
Clean the milk system (requires cleaning tablet) |
jura.power_off |
Put the machine into standby (TT237W ignores this) |
jura.restart |
Reboot the WiFi dongle |
jura.cancel |
Cancel the current process/brew |
jura.skip_quality_step |
Skip the current quality-assistant step (scope: one/all) |
jura.milk_cooler_status |
Read the milk-cooler temperature (response) |
jura.restart_dongle |
Reboot the WiFi dongle (library-gated variant) |
jura.special_counters |
Read special-function counters (response) |
All services accept entity_id (any Jura entity) or config_entry_id to
target a specific machine. Brewing returns the raw command result as the
service response.
Destructive registry entries that can lock you out of the machine
(skip-quality-step, set-pin, set-ssid, set-password, raw) are
deliberately not exposed as HA services. Use the upstream
jura-connect CLI if you need them.
- In HACS → Integrations → ⋮ → Custom repositories, add
https://github.com/makefu/jura-connect-hassas type Integration. - Install Jura Connect from the list.
- Restart Home Assistant.
Copy custom_components/jura/ into your Home Assistant
config/custom_components/ directory and restart.
- Settings → Devices & Services → Add Integration → Jura Connect.
- Choose Search for machines on the network (recommended) or Enter the IP address manually. Discovery shows a progress spinner; empty results route to manual entry.
- When prompted, press OK on the coffee machine to accept the pairing. The pairing dialog shows a spinner with a timeout of 60 seconds. The resulting auth-hash is persisted to the config entry; subsequent reconnects skip the on-machine confirmation.
- Pick your machine model from the dropdown. Auto-detection
pre-selects the right entry when the WiFi dongle replied to UDP
discovery (the broadcast carries the article number, which maps to an
EF code via the bundled catalogue). Otherwise choose your model — for
example
S8 (EB) [EF1091]— or pick Use baseline (no profile) for a generic EF536 fallback. Models without a profile won't get per-recipe brew counters or machine-setting entities.
alias: Morning espresso
trigger:
- platform: time
at: "07:00:00"
condition:
- condition: state
entity_id: binary_sensor.jura_connectivity
state: "on"
- condition: state
entity_id: binary_sensor.jura_alert_fill_water
state: "off"
action:
- service: jura.brew
target:
entity_id: sensor.jura_status
data:
recipe: "01"alias: Coffee machine needs cleaning
trigger:
- platform: numeric_state
entity_id: sensor.jura_service_cleaning_level
above: 95
action:
- service: notify.mobile_app
data:
message: "Cleaning cycle due — service level {{ states('sensor.jura_service_cleaning_level') }}%"alias: Power-saving overnight
trigger:
- platform: time
at: "22:00:00"
action:
- service: select.select_option
target:
entity_id: select.jura_setting_auto_off
data:
option: "15min"(Exact entity_id slugs depend on your machine's host / conn-id — check the device page after setup.)
See AGENTS.md for the full development guide. TL;DR:
nix develop -c pytest tests/ -v
nix develop -c ruff check
nix build
./result/bin/jura-connect-ha --versionjura-connect— the reverse-engineered protocol library this integration wraps. Most of the heavy lifting (handshake, status decoding, command registry, per-machine XML catalogue) lives upstream.- The J.O.E. (Jura Operating Experience) Android app, from which the WiFi protocol was originally derived.