Energy Tracking¶
Track electricity consumed by every print and across the whole lifetime of a printer, then multiply by your kWh rate to get cost. The numbers come straight from a smart plug's energy register — not from estimates.
What It Is¶
BamDude reads two values from each smart plug:
- Live wattage — how many watts the printer is drawing right now.
- Lifetime energy counter — total kWh the plug has measured since it was reset.
When a print starts, the lifetime counter is captured on the archive row as energy_start_kwh. When the print finishes, BamDude reads the counter again and stores the delta as energy_kwh. The starting value lives on the archive row, not in memory — so a backend restart mid-print does not lose the baseline, and the delta is still computed when the print completes. (If the print ends during the outage, see "If the print ends while BamDude is down" below.)
Figures recorded before 0.5.3 understate prints started outside BamDude
"At print start" has always been the intent, and for prints BamDude dispatched itself it was also the fact. For a print started from the printer's screen or sent straight from a slicer, the baseline used to be read once the archive row existed — and that row was only created after BamDude had fetched the print's 3MF back off the printer, which on a P1S measured 8m40s. Everything the printer drew in the meantime, bed heating included, was left out. The error was always in the same direction, so it never averaged out. Historical rows are not recalculated; there is nothing to recalculate them from.
For lifetime / date-range views, an hourly background loop snapshots each plug's lifetime counter into smart_plug_energy_snapshots. Date-range totals are then computed as last_snapshot_in_range − last_snapshot_before_range per plug.
Permission Required
Reading energy data requires stats:read. Tracking starts automatically once a smart plug is bound to a printer — there is no separate "enable energy tracking" toggle.
Requirements¶
Energy tracking needs a smart plug with kWh metering wired between the wall outlet and the printer. The plug type, power monitoring details, and configuration steps are covered in Smart Plugs.
| Plug Type | kWh Metering | Notes |
|---|---|---|
| Tasmota | Native HTTP energy endpoint | |
| Home Assistant | Bind a HA energy sensor entity | |
| REST / Webhook | Configure JSON path to extract kWh | |
| MQTT | Configure MQTT topic + JSON path |
Plugs without an energy register (basic on/off plugs) still work for power control, but their archive rows will have NULL energy_kwh and won't contribute to lifetime totals.
Per-Print kWh¶
Captured on PrintArchive:
| Column | Captured When | Meaning |
|---|---|---|
energy_start_kwh |
At print start | Plug's lifetime counter at the moment the print began |
energy_kwh |
At print complete | (end_counter − energy_start_kwh), the kWh this single print consumed |
energy_cost |
At print complete | energy_kwh × cost_per_kwh (denominated in your configured currency) |
Restart-resilient. Because energy_start_kwh is persisted to the archive row inside the same transaction that records started_at, a backend crash or container restart mid-print does not lose the baseline — the next on_print_complete will compute the delta correctly.
If the print ends while BamDude is down, there is no on_print_complete to run: the printer finishes alone and BamDude only learns about it on the next start. The startup sweep closes that print and reads the counter then, so the archive still gets a figure — but the meter kept counting through however long the printer sat idle in between, so that idle draw is inside the number. Such archives carry extra_data.energy_is_approximate = true, and the log line says (approximate — recovered print). The alternative was leaving the field empty, which reads as "this print used no power" — further from the truth than a modest over-count.
Failed and cancelled prints still record energy. A 6-hour print that fails at hour 4 still consumed 4 hours of electricity — the delta is still meaningful and is still written.
Lifetime kWh and Date Ranges¶
The hourly snapshot loop (SmartPlugManager._snapshot_loop) records one row per plug into smart_plug_energy_snapshots. A snapshot is also written at the start of every print, from the same reading that seeds the archive's baseline — so a print's own start is a real boundary in the history rather than whichever hour the loop last happened to fire:
| Column | Meaning |
|---|---|
plug_id |
FK to the smart plug |
recorded_at |
UTC timestamp of the snapshot |
lifetime_kwh |
Plug's lifetime energy register at that moment |
For a date range [date_from, date_to], BamDude computes per plug:
The max(0, …) clamps to zero when the lifetime counter has been reset (e.g. after a plug factory-reset) so you never get negative energy.
Which day is "today". Dates you pick, and "energy used today", are resolved in your timezone -- the browser tells BamDude which one you are in. Snapshots are stored in UTC, as above, and the day boundary is converted before the query. Work BamDude does on its own schedule -- nightly backups, digests -- keeps using the server's timezone, which is what those should follow. Callers that cannot send a timezone (the Telegram bot, API keys, webhooks) are answered in the server's.
Cost Calculation¶
Cost is a single-rate calculation against the lifetime / per-print delta:
Configure the rate in Settings → System → Energy:
| Setting | Description |
|---|---|
energy_cost_per_kwh |
Your electricity price per kWh (default 0.15) |
The numeric rate is dimensionless — display the cost in whatever currency matches your real tariff. BamDude doesn't convert currencies; it just multiplies.
Energy Tracking Mode¶
Settings → System → Energy → Energy Tracking Mode (energy_tracking_mode):
| Mode | What "Energy used" Means on Stats |
|---|---|
print |
Sum of per-archive energy_kwh values over the date range. Excludes idle, standby, chamber-only heating. Pure printing cost. |
total (default) |
Lifetime plug counter via snapshot range — last_in_range − baseline_before_range. Includes idle / standby / chamber heating / firmware-update sessions / anything the printer drew while plugged in. |
Pick print if you bill customers per job. Pick total if you want to know what your printer farm actually costs to keep running.
"Warming-Up" Indicator¶
The total mode needs at least one snapshot before the start of your selected range to compute a baseline. On a fresh install, immediately after upgrading to a build that ships snapshot support, or right after Last 7 days is shifted into a window with no prior snapshot, that baseline doesn't exist yet.
When this happens, the Stats page shows a yellow warning icon next to Energy Used and Energy Cost:
Still warming up — at least one plug doesn't have a snapshot from before the start of your range.
The icon disappears as soon as enough snapshots exist. No configuration needed; the system is just collecting data. Backend flag: energy_data_warming_up=True on the stats response.
Tibber / Octopus / Dynamic Tariff Integration¶
If you're on a dynamic electricity tariff (Tibber, Octopus, Nordpool, …), push the live rate into BamDude from Home Assistant — every cost calculation will then use the current rate instead of a static value.
1. Create an API key¶
Settings → API Keys → Create, and flip the Update electricity price toggle on the key's row. This is a narrowly-scoped opt-in for POST /settings/electricity-price — it does not grant general settings-write capability (the wider PATCH /settings route is unchanged). Copy the key.
Older docs referenced PATCH /settings
The general PATCH /settings endpoint still works for API keys, but it exposes the entire settings payload (SMTP / LDAP / MQTT credentials, HA token, every UI rendering knob) — a much wider surface than the dynamic-tariff use case needs. The new POST /settings/electricity-price endpoint accepts only {energy_cost_per_kwh}, returns the full settings response so HA can confirm the new value applied, and is gated by the per-key Update electricity price toggle so admins explicitly opt in. Configs pointing at the legacy URL keep working; switch to the narrow URL on your next config refresh.
2. Add a REST command in HA¶
Add to your configuration.yaml:
rest_command:
bamdude_electricity_price:
url: "http://YOUR_BAMDUDE_IP:8000/api/v1/settings/electricity-price"
method: POST
headers:
X-API-Key: "YOUR_API_KEY"
content_type: "application/json"
payload: '{"energy_cost_per_kwh": {{ states("sensor.electricity_price") }}}'
3. Trigger the REST command on price change¶
automation:
- id: bamdude_push_electricity_price
alias: "Update BamDude electricity price"
mode: restart
trigger:
- platform: state
entity_id: sensor.electricity_price
for: "00:00:05"
condition:
- condition: template
value_template: >
{{ states('sensor.electricity_price')|float(none) is not none }}
action:
- service: rest_command.bamdude_electricity_price
| Provider | Typical sensor entity |
|---|---|
| Tibber | sensor.tibber_prices (current price attribute) |
| Octopus Energy | sensor.octopus_energy_electricity_current_rate |
| Nordpool | sensor.nordpool_kwh_* |
Verify the sensor returns a number
BamDude expects a numeric value for energy_cost_per_kwh. If your sensor returns a string with a currency symbol, adjust the template ({{ states('sensor.x')|float }}) before pushing.
For more on the Home Assistant integration architecture see Smart Plugs → Home Assistant.
Stats-Page Widgets¶
The Statistics page surfaces energy in three places:
- Energy used (kWh) for the selected date range, respecting
energy_tracking_mode - Energy cost in your configured currency
- Per-printer breakdown so you can see which machine is drawing most
Charts and totals are kept in sync with the print vs total switch — toggling rebuilds them server-side. See Statistics for the full widget tour.
Related¶
- Smart Plugs — plug types, configuration, HA / Tasmota / REST / MQTT setup
- Archiving —
energy_kwh/energy_costfields on each archive row - Statistics — energy widget, cost charts, date-range filters
- Print Queue — auto-power-off after print + smart-plug-driven queue automation
- Export — CSV/XLSX with per-print energy + cost columns
Tips¶
Use your real tariff
Pull the all-in rate from your last electricity bill (energy + delivery + taxes + fees). A "headline" rate from your supplier's website usually understates the true cost.
Failed prints still cost
energy_kwh is recorded for failed and cancelled archives too — that's another data point arguing for Failure Analysis and Obico. Every fail is real money on the meter.
Snapshot baselines need uptime
The hourly snapshot loop only runs while BamDude is running. If you stop the container for two days and ask for a 7-day total, the missing 48h shows up as a flat baseline — the warming-up icon will surface this.
Print mode for invoicing, total mode for ROI
Switch to print mode when exporting customer invoices — they shouldn't pay for your standby. Switch to total mode when calculating whether the farm is paying for itself.