Skip to content

Instantly share code, notes, and snippets.

@adampetrovic
Last active May 13, 2025 18:10
Show Gist options
  • Save adampetrovic/448b276d591ee1c1a2ebd9e52119c9f8 to your computer and use it in GitHub Desktop.
Save adampetrovic/448b276d591ee1c1a2ebd9e52119c9f8 to your computer and use it in GitHub Desktop.
Homebrew by Weight - La Marzocco Linea Micra

This is my 'homebrew' version of the brew-by-weight functionality that comes with a Linea Mini plus the LaMarzocco + Acaia proprietary scales. Given I have a Micra, this functionality isn't supported out of the box.

It uses:

How it works:

  • input_number.coffee_target_weight: used to set the desired weight
  • input_number.coffee_drip_duration: used to estimate how long (in secs) the coffee continues to drip after the pump is stopped.
  1. Turn on the scales and wait for the connected symbol to show on the scales (next to the timer).
  2. Start the shot. Homeassistant will tare the scales automatically and start the timer on the scales.
  3. Once the shot timer reaches 10 seconds, the brew-by-weight automation should trigger
  4. The automation continues to monitor the current weight and flow rate from the scales, continually updating the coffee_estimated_drip_weight template sensor.
  5. Once the current weight reaches the threshold of (current_weight + coffee_estimated_drip_weight >= target_weight) it turns off the machine (only way to stop the pump) and turns it back on.

Adjust the input_number.coffee_drip_duration over time to get more accurate.

external_components:
- source:
type: git
url: https://github.com/adampetrovic/esphome-acaia-component
ref: log_flow
refresh: 0s
esphome:
name: coffee-controller
friendly_name: coffee-controller
esp32:
board: esp32-s3-devkitc-1
framework:
type: arduino
logger:
level: DEBUG
api:
ota:
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: "Coffee-Controller"
password: "password"
bluetooth_proxy:
active: True
esp32_ble_tracker:
ble_client:
- mac_address: <mac address of your scales>
id: acaia_client
acaia:
ble_client_id: acaia_client
id: acaia_1
binary_sensor:
- platform: acaia
connected:
name: Acaia Connected
id: acaia_connected
sensor:
- platform: acaia
weight:
id: scale_sens
name: Scale weight
accuracy_decimals: 1
unit_of_measurement: g
filters:
- lambda: return x * 1000;
flow:
name: Scale flow
id: flow_sens
button:
- platform: template
name: Tare Scales
id: scale_tare
icon: "mdi:scale"
on_press:
- acaia.tare: acaia_1
- platform: template
name: Start Timer
id: start_timer
icon: "mdi:timer-outline"
on_press:
- acaia.start_timer: acaia_1
- platform: template
name: Stop Timer
id: stop_timer
icon: "mdi:timer-outline"
on_press:
- acaia.stop_timer: acaia_1
- platform: template
name: Reset Timer
id: reset_timer
icon: "mdi:timer-outline"
on_press:
- acaia.reset_timer: acaia_1
input_number:
coffee_target_weight:
name: Coffee Target Weight
unit_of_measurement: g
initial: 42
min: 10
max: 60
step: 1
coffee_drip_duration:
name: Coffee Drip Duration
unit_of_measurement: s
initial: 2.5
min: 0
max: 10
step: 0.1
sensor:
- platform: template
sensors:
coffee_estimated_drip_weight:
unique_id: coffee_estimated_drip_weight
friendly_name: Coffee Post-Shot Drip Weight
unit_of_measurement: "g"
availability_template: "{{ states('sensor.coffee_controller_scale_flow') | float > 0 }}"
value_template: >
{{ (states('input_number.coffee_drip_duration') | float * states('sensor.coffee_controller_scale_flow') | float) | round(2) }}
automation:
- id: start_shot_setup_scales
alias: "[Coffee] Setup scales on shot start"
mode: single
trigger:
- platform: state
entity_id: binary_sensor.micra_brewing_active
to: "on"
condition:
- "{{ is_state('binary_sensor.coffee_controller_acaia_connected', 'on') }}"
action:
- service: button.press
target:
entity_id: button.coffee_controller_reset_timer
- service: button.press
target:
entity_id: button.coffee_controller_start_timer
- service: button.press
target:
entity_id: button.coffee_controller_tare_scales
- id: stop_shot_setup_scales
alias: "[Coffee] Stop scales on target weight"
mode: single
trigger:
- platform: template
value_template: "{{ states('sensor.coffee_controller_scale_weight') | float >= states('input_number.coffee_target_weight') | float }}"
condition:
- "{{ states('sensor.micra_shot_timer') | float >= 10 }}"
action:
- service: button.press
target:
entity_id: button.coffee_controller_stop_timer
- id: brew_by_weight
alias: "[Coffee] Brew by Weight"
mode: single
trigger:
# make sure we're actually brewing a valid shot
- platform: template
value_template: "{{ states('sensor.micra_shot_timer') | float >= 10 }}"
condition:
- "{{ is_state('binary_sensor.coffee_controller_acaia_connected', 'on') }}"
action:
- wait_template: "{{ states('sensor.coffee_controller_scale_flow') | float > 0 }}"
timeout:
seconds: 5
continue_on_timeout: false
- repeat:
# stop when actual_weight >= target_weight - estimated_drip_weight
# estimated_drip_weight is calculated above
until: "{{ (states('sensor.coffee_controller_scale_weight') | float >= (states('input_number.coffee_target_weight') | float - states('sensor.coffee_estimated_drip_weight') | float))}}"
sequence:
- delay:
milliseconds: 100
- service: switch.turn_off
target:
entity_id: switch.coffee_machine
- delay:
seconds: 2
- service: switch.turn_on
target:
entity_id: switch.coffee_machine
@Subtletree
Copy link

Hey @adampetrovic, cheers for this!

With the new cloud only communication with the machine are your timings still working? I guess I'll just adjust the drip duration to include the latency with shutting off the machine.

@adampetrovic
Copy link
Author

@Subtletree The only thing impacted by the new firmware and its delay is the trigger to the brew_by_weight automation. That being delayed by 4 seconds won't really have a measurable impact on the result. Really we only care about brewing being active at some point, so we can start calculating the coffee_estimated_drip_weight.

The logic of brew by weight in this case is only dependent on sensor.coffee_controller_scale_flow being accurate. This is a value that is calculated internally in the esp32 component (esphome-acaia-component).

@adampetrovic
Copy link
Author

adampetrovic commented May 6, 2025

It's also worth noting that the reliance on the ESP32 controller firmware is optional now. If instead, you use ESP32 bluetooth proxies in your HomeAssistant installation, you can use the Acaia component bundled in the HomeAssistant core now. Which supports everything we're doing here: https://www.home-assistant.io/integrations/acaia/

Volume flow rate: Calculates the current flow rate (in mL/s) while brewing.
Weight: The weight currently shown on the scale.

I will look to provide a blueprint to perform brew-by-weight when I get a chance, which should significantly lower the barrier to entry for people to get started.

@StefanoGaliani
Copy link

It's also worth noting that the reliance on the ESP32 controller firmware is optional now. If instead, you use ESP32 bluetooth proxies in your HomeAssistant installation, you can use the Acaia component bundled in the HomeAssistant core now. Which supports everything we're doing here: https://www.home-assistant.io/integrations/acaia/

Volume flow rate: Calculates the current flow rate (in mL/s) while brewing.
Weight: The weight currently shown on the scale.

I will look to provide a blueprint to perform brew-by-weight when I get a chance, which should significantly lower the barrier to entry for people to get started.

This would be great. Thank you very much Adam

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment