mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-08-11 00:30:12 -04:00
Post work PR #2608
This commit is contained in:
parent
be97f2f5be
commit
454ce3ceec
1 changed files with 1 additions and 0 deletions
|
|
@ -62,6 +62,7 @@ All notable changes to Bambuddy will be documented in this file.
|
|||
- **Energy Summary stuck at zero for Yesterday and Total on REST smart plugs — and the Statistics energy figure with it (#2539, reporter @R3play210)** — A Shelly Plug S Gen3 wired up over the REST integration showed live power and a Today figure that climbed, but Yesterday and Total never moved off zero, through five days of printing. **The bug.** `RESTSmartPlugService.get_energy()` returned a dict with two keys, `power` and `today`. It never set `yesterday` or `total` at all, so `SmartPlugEnergy` defaulted them to null and the summary card summed nothing. Tasmota returns all three; Home Assistant returns two; REST returned one. **The number that looked right was also wrong.** A Shelly has no notion of "today" — its only energy figure is `aenergy.total`, a lifetime counter in watt-hours that climbs forever and never resets. Bambuddy had a single energy field, so the reporter put the lifetime counter in it, and line 230 filed it under `today`. It *looked* correct because it grows; it just never dropped back to zero at midnight. The one figure he trusted was the least trustworthy of the four. **It broke more than the card.** With `total` never populated, the hourly snapshot recorder skipped the plug outright (its own comment said so: *"REST plugs that only expose today can't be used for cumulative snapshots"*), `_sum_live_plug_totals()` summed zero, and since the reporter's `energy_tracking_mode` is `total`, the **Statistics page's energy figure was zero too** — he simply hadn't got to it yet. **The fix.** A REST plug now says which counter it has: `rest_energy_path` still means "energy used today", and a new `rest_energy_total_path` means "lifetime counter that never resets". A Shelly has only the latter; a Tasmota behind a REST bridge has both; both are read from one HTTP fetch when they share a URL. Then, because the snapshot table already records that lifetime counter hourly, **Today and Yesterday are derived from it**: today = the counter now minus its value at the last local midnight, yesterday = that midnight's value minus the one before. So a Shelly gets all four numbers with no new device capability — and Home Assistant's permanently-null Yesterday is fixed for free. Today appears after the first midnight the install lives through, Yesterday after the second; a counter that goes backwards (factory reset zeroes `aenergy.total`) reports nothing rather than a negative. **Local midnight, not UTC.** With `TZ=Europe/Berlin` a UTC boundary would roll Today over at 02:00 wall-clock. The snapshot loop now ticks on the *local* hour instead of every 3600s from boot, so a reading lands exactly on the day boundary — including in the half-hour-offset zones (India, Nepal) where local midnight isn't on a UTC hour at all. Previously the last snapshot before midnight could be up to an hour early, and an hour of a printer's draw is real watt-hours to lose off the day. **Collateral: the whole smart-plug subsystem was broken on Postgres.** Every `DateTime` column in the smart-plug tables is naive and holds UTC, but the code wrote *aware* datetimes into them. SQLite tolerates that — its bind processor reads the fields and drops the offset — which is why it went unnoticed. asyncpg does not: it raises `DataError: invalid input for query argument`. So on Postgres every energy-snapshot capture raised (silently, inside the loop's `except`), leaving the snapshot table empty and the date-filtered energy stat permanently zero, and every plug status poll raised on `last_checked`. Postgres is what Bambuddy recommends for multi-printer installs, so this was not a corner. All smart-plug timestamps are now naive UTC via a shared `utcnow_naive()` / `to_naive_utc()`, and the snapshot-delta query normalises its bounds the same way. **Tests.** 8 cases on the derivation (today and yesterday from the counter; yesterday absent until two midnights have passed; nothing derivable before the first; a counter reset reports nothing rather than a negative; another plug's snapshots are not borrowed; a device-reported figure is never overwritten by our arithmetic). 4 on the REST driver, using the reporter's own `Switch.GetStatus` payload (the lifetime counter lands in `total` and *not* in `today`; a plug reporting both keeps them apart; a total path alone is enough to read energy at all; both counters share one HTTP fetch). 4 more pin the Postgres-unsafe datetime — mutation-verified: reintroducing the aware timestamp fails the guard. Migration applied and re-applied against a real Postgres to confirm it is idempotent and defaults to NULL. **Existing REST users:** if your Energy JSON Path points at a cumulative counter (anything from a Shelly does), move it to the new **Energy JSON Path (lifetime)** field — the form and the wiki now say which field wants which counter.
|
||||
|
||||
### Added
|
||||
- **Russian (Русский) UI translation (#2608, contributor @pterodaktil02)** — Bambuddy's interface is now fully available in Russian, bringing the total to 11 languages. Pick it under Settings → General → Language. The translation covers the whole UI — printer controls and statuses, build plate and bed, filament, and AMS — with context-appropriate terminology throughout, and preserves every interpolation placeholder so counts, names, and progress values render correctly.
|
||||
- **"Slice as designed" — keep a MakerWorld author's own settings when you slice server-side (#2611, reporter @kpp39)** — When you slice a project 3MF through Bambuddy, the SliceModal makes you pick a printer / process / filament triplet, and the slicer applies those with `--load-settings` — which *overrides* whatever the designer baked into the file's `Metadata/project_settings.config`. So a MakerWorld model set up for 5 walls came out at the picked profile's 2, and the reporter's own re-posted files lost their tweaks too. That override is correct for the flow's main job — re-slicing someone else's design for *your* printer and AMS, especially across models (an H2D design onto an X1C) *needs* the bed size and filaments swapped — but it left no way to say "just slice it the way the author set it up." **What's new.** When the source 3MF carries embedded settings **and** the picked printer matches the design's target model, the modal offers a **Use the file's built-in settings** checkbox. Tick it and Bambuddy slices with no `--load-settings` override, so the designer's walls / infill / filament choices drive the result; all four preset controls (printer, process, filament, bed type) grey out to show they're bypassed — the printer included, since it's unused on this path and changing it would only pull you off the design's target and hide the toggle again. **The printer-match gate is deliberate.** Honouring embedded settings only makes sense when your printer *is* the design's printer — applying them across models would drop the object on the wrong bed, which is the whole reason the profile path exists — so the toggle simply isn't offered otherwise, and there's no cross-printer re-targeting on this path. Filament comes from the file too, not your AMS picks; the hint says so. **Under the hood** this reuses the existing embedded-settings slice path (previously only a crash fallback) as a first-class, user-selectable mode; the response already flagged `used_embedded_settings`. **Not** in scope: merging a picked filament *over* the designer's other settings — that needs per-key precedence and is a separate future enhancement. **Scope.** One backend request flag + one branch, one gated frontend checkbox. No DB migration, no new permission, no new setting. Two new i18n keys (`slice.useEmbedded`, `slice.useEmbeddedHint`) translated in all 11 locales.
|
||||
- **A paused AMS runout now names the physical slot the printer is actually waiting for (#2587, reporter @Jostxxl)** — When a spool runs out mid-print, Bambuddy showed the firmware's generic HMS text — "insert a new filament into the same AMS slot" — which is exactly wrong when AMS Filament Backup is on: the firmware won't re-accept the depleted slot and advances to the next compatible one, so "the same slot" sends the operator to the wrong place. On the reporter's farm this meant reinserting into Slot 2 (where it ran out) did nothing, and the print only resumed after moving the spool to Slot 3 — with no on-screen hint that Slot 3 was what the printer wanted. **Root cause.** The printer's AMS payload carries `tray_tar` (the slot the paused print now expects) and `tray_pre` (the slot that ran out) right next to `tray_now`, but Bambuddy parsed `tray_now` only and dropped the other two at ingest, so "which slot does the print expect" never reached the API or the UI. **What changed.** `tray_tar`/`tray_pre` are now captured on printer state and, while the print is **paused**, resolved to global tray IDs and surfaced on the status payload (both the REST poll and the live WebSocket push) as `expected_tray` / `previous_tray`. The AMS graphic highlights the expected slot with a pulsing amber ring (and a down-arrow badge) and marks the ran-out slot in red, and the HMS error is re-described to name them directly — e.g. "Filament ran out in AMS-A · Slot 2. The printer is now waiting for compatible filament in AMS-A · Slot 3. Insert a spool into AMS-A · Slot 3, then select Retry." **Honest when it can't tell.** On a single regular AMS the reported slot is already the global ID; on multi-AMS it's a local slot that's resolved against the print's snow-encoded mapping field, and AMS-HT IDs (128–135) pass through. When the slot can't be resolved unambiguously (multi-AMS with no usable mapping), the graphic highlights nothing and the message says so — "Bambuddy could not determine which slot the printer now expects — check the printer screen" — rather than pointing at a guess. User AMS friendly-names are honored in the labels. **Scope.** Guidance is populated only while paused, so a healthy print's normal target churn never highlights a slot or spams the log. Backend resolver, the ingest parse, and the modal re-description are covered by new unit/component tests; the runout copy is translated in all 11 locales.
|
||||
- **The sponsor surfaces now ask a print farm a different question than they ask a hobbyist** — Since the in-app sponsor banner and milestone toast shipped in v0.2.4.8, both have made exactly one ask, to everyone: chip in a few dollars to keep Bambuddy independent. That ask works — new sponsorships went from 0.40/day to 1.40/day in the fifteen days after the release, and clicks through to GitHub Sponsors rose 4.3x on a *falling* web traffic base. But it is the wrong ask for part of the audience. Someone running twelve printers as a business does not want to donate $5; they want a support contract, an invoice, and somebody accountable when the line stops. They were being shown a donation button and, unsurprisingly, ignoring it. **What changed.** At **5 or more configured printers** the Settings → General banner and the milestone toast both make the commercial ask instead — priority support, commercial licensing, invoicing — and link to the new **bambuddy.cool/business.html** rather than the sponsor tiers. Below that, nothing changes at all. It is the same single interruption either way: same milestones, same 14-day cooldown, same one-toast-per-session guard. Only the ask changes, so nobody sees more nagging than before. **Configured printers, not active ones.** The count deliberately ignores `is_active`, which is the maintenance-mode flag rather than a fleet-size signal. A farm with eight machines and five of them on the bench for nozzle swaps is still a farm — filtering on `is_active` would have counted three, downgraded them to the hobbyist pitch, and done it precisely when they were having the worst day. **The page concedes the licence up front.** business.html opens by stating plainly that Bambuddy is AGPL-3.0, that running it inside your own business costs nothing, and that no licence is required no matter how many printers you have — because that is true, and a page that implied otherwise would be a lie the audience would catch immediately. What it then offers is the set of things a licence cannot give you: priority support with a named contact and agreed response times, commercial licensing for the narrow case where you actually need it (redistribution, OEM, shipping Bambuddy on an appliance), fleet deployment and custom development, and operator training. No price list — those conversations are scoped individually. **Attribution is preserved.** Both surfaces keep their existing Matomo `?from=` tags (`app-settings`, `app-toast-{milestone}`), so the business funnel is measurable from day one on the same dashboards as the personal one, and the split between the two is visible without any new instrumentation. **No telemetry was added**, and none is needed: fleet size is read from the printers list the app already has cached. **Scope.** Frontend only — no backend change, no schema change, no migration, no new permission, no new setting. The audience split is one shared helper (`utils/fleetAudience.ts`) so the threshold lives in exactly one place. **Tests.** 7 new cases: the boundary in both directions (4 printers → personal, 5 → business); the maintenance-mode trap (8 printers with 5 inactive still reads as business); the cold-cache race (the toast waits for the fleet to load rather than defaulting to zero printers and pitching a farm as a hobbyist); both banner variants including the assertion that the commercial copy *replaces* the donation copy rather than sitting beside it; and the `?from=` tag surviving on both paths. All 7 mutation-verified — forcing the threshold out of reach, or dropping the fleet-load gate, fails them. **i18n.** 4 new keys (`sponsors.toastBusiness`, `businessCta`, `businessTitle`, `businessTagline`) translated in all 11 locales; parity 5616 keys.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue