diff --git a/CHANGELOG.md b/CHANGELOG.md index f74aa1c3a..e051e7635 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ All notable changes to Bambuddy will be documented in this file. ## [0.2.5b1] - Unreleased +### Changed +- **Cloud login: corrected the access-token hint to reflect that Bambu Lab no longer surfaces the token in any UI, and called out the China-region constraint explicitly (#1396)** — Reporter wintsa123 filed that China-region users can't log into Bambuddy. The code path itself is fine: PR #1013 (April) already added the China-region selector to the login form and routes token validation to `api.bambulab.cn`. The actual gap was documentation. The old in-app `accessTokenHint` said "Paste your Bambu Lab access token (from Bambu Studio)" — but Bambu Studio never exposed the token in any UI, and the profile page on `bambulab.com` that used to show it is gone. For China-region accounts the email/password flow is fundamentally unusable because those accounts are bound to phone numbers, not email — token login is the only path, and the hint didn't say so. Updated `accessTokenHint` in all 8 locales (en/de/fr/it/ja/pt-BR/zh-CN/zh-TW) to state that China accounts must use this path and point at the wiki for the MakerWorld-cookie retrieval procedure. Wiki page `features/cloud-profiles.md` also rewritten under "Access Token Login": adds a "Region: China must use token login" note, replaces the dead "from Bambu Studio" guidance with the working MakerWorld-cookie method (browser DevTools → Application → Cookies → `token`), keeps the Python-script alternative for global-region accounts, and flags that the cookie value is sensitive. No backend changes — the token-validation endpoint accepts both `global` and `china` regions and routes to the right API host already. + ### Fixed - **Virtual Printer (queue / immediate / review modes): AMS data flickered or disappeared in BambuStudio between pushalls on P1S/A1 targets (#1387)** — Reporter vmhomelab ran a Print Queue VP against a P1S, opened BambuStudio, and saw the External Spool only — no AMS. Toggling Auto-Dispatch (which triggers a VP restart) made AMS briefly appear, then it reverted to defaults. Proxy Mode worked fine. The earlier #1371 sticky-keys fix only handled one of two Bambu firmware incremental-push shapes: it preserved cached AMS when the incoming push *omitted* the `ams` key entirely (H2D's common incremental shape). The reporter's P1S firmware (01.09.01.00) instead sends incrementals with the `ams` key present but the inner `ams.ams` array stripped — `{ams_status: 1, humidity: 2}` instead of `{ams: [...], ams_status: 1}`. To the previous sticky-keys check that read as "key present, leave new state alone," so the bridge cache got overwritten with the stripped blob; the slicer's next 1 Hz read saw `ams` with no unit list and fell back to the "no AMS" default render. Toggling Auto-Dispatch restarted the VP and got a fresh pushall in; the next P1S incremental stripped it again. (H2D rarely hits this — its incrementals typically don't carry `ams` at all, so #1371 alone was enough there. The reporter's same-VP-architecture pinging both an H2D and a P1S would observe the H2D works while the P1S doesn't, which is exactly the split that surfaced this.) Fix is a deep-merge applied to the `ams` key inside the bridge cache, mirroring the structure Bambuddy itself already does in `bambu_mqtt.py::_handle_ams_data` (which is why Bambuddy's own AMS display stays coherent on the same firmware): scalar fields like `ams_status` and `humidity` take the new value, but the `ams.ams` array is merged unit-by-unit on `id`, each unit's `tray` array is merged tray-by-tray on `id`, and units / trays the incremental doesn't mention survive intact from the cached full state. A tray-targeted incremental during a print like `{ams: [{id: 0, tray: [{id: 0, state: 11}]}]}` now updates that one tray's state without nuking the other three trays' tray_type/tray_color. Helper added as `_merge_ams_dict` in `backend/app/services/virtual_printer/mqtt_bridge.py` next to `_ip_to_uint32_le`, called from the existing sticky-keys block. Three new regression tests under `TestPushStatusCache` in `backend/tests/unit/test_vp_mqtt_bridge.py` cover the status-only partial (the reporter's exact reproduction), the multi-AMS unit-level merge, and the multi-tray merge. The existing `test_incoming_ams_update_replaces_cached_ams` still passes — fresh full updates still take effect, the merge only protects the cache from stripped incrementals. 32 tests total in that file, all green. Verified the cross-subnet topology from the report (printer / Bambuddy / slicer each on a different /24) is incidental: the symptom is the same regardless of subnet once the partial-shape arrives; the latency just makes the "empty cache when slicer first connects" race more visible. ProxyMode is unaffected because Proxy is raw byte-forwarding rather than a cached-as-base mirror — it never had this class of bug. - **Quick Stats showed Filament Cost = 0 and empty Time Accuracy on pre-upgrade data after the 0.2.4.1 stats rewrite (#1390)** — Reporter IndividualGhost1905 upgraded to 0.2.4.1 (which shipped the per-event aggregation rewrite from #1378) and saw the Stats page split between consistent values (Total Prints / Print Time / Filament Used / Energy / Success Rate matched the archive list) and zero-or-empty ones (Filament Cost, Time Accuracy). Inconsistency was a migration gap: #1378 added six columns to `print_log_entries` — `archive_id`, `cost`, `energy_kwh`, `energy_cost`, `failure_reason`, `created_by_id` — but **didn't backfill any of them**. So every pre-upgrade log entry kept NULL on all six. The new Quick Stats query sums `PrintLogEntry.cost` (gets 0 for legacy data); the time-accuracy query joins `PrintArchive ON archive_id` (drops every legacy run from the average). Counts and per-row fields that already existed pre-#1378 (`status`, `duration_seconds`, `filament_used_grams`) kept working — which is why some panels looked right and others didn't. Fix is a two-step backfill in `run_migrations` next to the existing column-add block (DML, runs inside `begin_nested()` not `_safe_execute` since the latter is documented "DDL only"): step 1 links each orphan log entry to its archive via `print_name + printer_id` (highest archive `id` wins on tiebreak — newest matches the overwrite-then-stop shape that pre-#1378 reprints left behind); step 2 copies `archive.cost / energy_kwh / energy_cost` onto the latest matching log entry per archive, **but only for archives where no log entry yet carries a cost**. That second clause is the idempotency anchor and also the double-count guard for users running this migration after #1378 has already written cost-bearing rows for new runs — those archives are left untouched. Earlier reprints stay NULL, matching the "first/latest writes, rest stay NULL" convention #1378 introduced. Sum across the legacy reprint chain reproduces sum-of-archive-cost exactly, so the Quick Stats Filament Cost column matches the pre-upgrade total instead of dropping to zero. SQL is plain ANSI — correlated UPDATE with `LIMIT 1` in the SET subquery, `WHERE id IN (SELECT MAX(id) ... GROUP BY archive_id HAVING SUM(CASE WHEN cost IS NOT NULL THEN 1 ELSE 0 END) = 0)` — verified end-to-end on both SQLite (4 unit tests in `test_print_log_backfill_migration.py`) and `postgres:16-alpine + asyncpg` (live container reproduction). For the other widgets the reporter listed (Printer Stats, Filament Trends, By Material, Success by Material, Color Distribution) — those still iterate the archives list on the frontend rather than calling /stats, so they read consistent pre-upgrade data and aren't part of this fix; the inconsistency the reporter saw between Quick Stats and those widgets resolves itself once the backfill brings Quick Stats in line. diff --git a/frontend/src/i18n/locales/de.ts b/frontend/src/i18n/locales/de.ts index c026ff272..19859cb9c 100644 --- a/frontend/src/i18n/locales/de.ts +++ b/frontend/src/i18n/locales/de.ts @@ -2860,7 +2860,7 @@ export default { checkEmail: 'Prüfen Sie Ihre E-Mail ({{email}}) für einen 6-stelligen Code', enterTotpHint: 'Geben Sie den 6-stelligen Code aus Ihrer Authenticator-App ein', accessToken: 'Zugriffstoken', - accessTokenHint: 'Fügen Sie Ihr Bambu Lab Zugriffstoken ein (aus Bambu Studio)', + accessTokenHint: 'Fügen Sie Ihr Bambu-Cloud-Zugriffstoken ein. Konten der Region China müssen diesen Weg nutzen (telefonnummerngebunden — kein E-Mail-Login). Im Wiki steht, wie Sie das Token aus den MakerWorld-Cookies auslesen.', back: 'Zurück', loginButton: 'Anmelden', verifyButton: 'Bestätigen', diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts index 449bc5144..299e6e512 100644 --- a/frontend/src/i18n/locales/en.ts +++ b/frontend/src/i18n/locales/en.ts @@ -2863,7 +2863,7 @@ export default { checkEmail: 'Check your email ({{email}}) for a 6-digit code', enterTotpHint: 'Enter the 6-digit code from your authenticator app', accessToken: 'Access Token', - accessTokenHint: 'Paste your Bambu Lab access token (from Bambu Studio)', + accessTokenHint: 'Paste your Bambu Cloud access token. China-region accounts must use this path (phone-bound — email login unavailable). See the wiki for how to retrieve the token from MakerWorld cookies.', back: 'Back', loginButton: 'Login', verifyButton: 'Verify', diff --git a/frontend/src/i18n/locales/fr.ts b/frontend/src/i18n/locales/fr.ts index 71ca9e026..fb953c76d 100644 --- a/frontend/src/i18n/locales/fr.ts +++ b/frontend/src/i18n/locales/fr.ts @@ -2849,7 +2849,7 @@ export default { checkEmail: 'Code envoyé à {{email}}', enterTotpHint: 'Entrez le code 2FA', accessToken: 'Jeton d\'accès (Access Token)', - accessTokenHint: 'Collez le jeton (depuis Bambu Studio)', + accessTokenHint: 'Collez votre jeton Bambu Cloud. Les comptes de la région Chine doivent passer par ici (liés à un numéro de téléphone — pas de connexion par e-mail). Le wiki explique comment récupérer le jeton depuis les cookies MakerWorld.', back: 'Retour', loginButton: 'Connexion', verifyButton: 'Vérifier', diff --git a/frontend/src/i18n/locales/it.ts b/frontend/src/i18n/locales/it.ts index db39b1847..186a3e7b0 100644 --- a/frontend/src/i18n/locales/it.ts +++ b/frontend/src/i18n/locales/it.ts @@ -2848,7 +2848,7 @@ export default { checkEmail: 'Controlla la tua email ({{email}}) per un codice a 6 cifre', enterTotpHint: 'Inserisci il codice a 6 cifre dalla tua app autenticatore', accessToken: 'Token di accesso', - accessTokenHint: 'Incolla il tuo access token Bambu Lab (da Bambu Studio)', + accessTokenHint: 'Incolla il tuo access token Bambu Cloud. Gli account della regione Cina devono usare questa via (vincolati al numero di telefono — login via e-mail non disponibile). Il wiki spiega come recuperare il token dai cookie di MakerWorld.', back: 'Indietro', loginButton: 'Accedi', verifyButton: 'Verifica', diff --git a/frontend/src/i18n/locales/ja.ts b/frontend/src/i18n/locales/ja.ts index 2fe8f3ffe..0552d87d2 100644 --- a/frontend/src/i18n/locales/ja.ts +++ b/frontend/src/i18n/locales/ja.ts @@ -2860,7 +2860,7 @@ export default { checkEmail: 'メール ({{email}}) に届いた6桁のコードを入力してください', enterTotpHint: '認証アプリの6桁のコードを入力してください', accessToken: 'アクセストークン', - accessTokenHint: 'Bambu Labのアクセストークンを貼り付け(Bambu Studioから取得)', + accessTokenHint: 'Bambu Cloud のアクセストークンを貼り付けてください。中国リージョンのアカウントはこの方法のみ利用可能です(電話番号認証のためメールログイン不可)。トークンの取得方法は MakerWorld の Cookie から(Wiki を参照)。', back: '戻る', loginButton: 'ログイン', verifyButton: '認証', diff --git a/frontend/src/i18n/locales/pt-BR.ts b/frontend/src/i18n/locales/pt-BR.ts index 8ffc80639..59bd364e2 100644 --- a/frontend/src/i18n/locales/pt-BR.ts +++ b/frontend/src/i18n/locales/pt-BR.ts @@ -2848,7 +2848,7 @@ export default { checkEmail: 'Verifique seu email ({{email}}) para um código de 6 dígitos', enterTotpHint: 'Digite o código de 6 dígitos do seu aplicativo autenticador', accessToken: 'Token de Acesso', - accessTokenHint: 'Cole seu token de acesso Bambu Lab (do Bambu Studio)', + accessTokenHint: 'Cole seu token de acesso da Bambu Cloud. Contas da região China precisam usar este caminho (vinculadas ao celular — login por e-mail indisponível). Veja no wiki como obter o token dos cookies do MakerWorld.', back: 'Voltar', loginButton: 'Entrar', verifyButton: 'Verificar', diff --git a/frontend/src/i18n/locales/zh-CN.ts b/frontend/src/i18n/locales/zh-CN.ts index e8d2f7d96..a6854670b 100644 --- a/frontend/src/i18n/locales/zh-CN.ts +++ b/frontend/src/i18n/locales/zh-CN.ts @@ -2848,7 +2848,7 @@ export default { checkEmail: '检查您的邮箱 ({{email}}) 获取 6 位验证码', enterTotpHint: '输入验证器应用中的 6 位代码', accessToken: '访问令牌', - accessTokenHint: '粘贴您的拓竹访问令牌(来自 Bambu Studio)', + accessTokenHint: '粘贴您的拓竹云访问令牌。中国大陆账号(绑定手机号、无邮箱登录)只能使用此方式登录。可从 MakerWorld 浏览器 Cookie 中获取令牌,详见 Wiki。', back: '返回', loginButton: '登录', verifyButton: '验证', diff --git a/frontend/src/i18n/locales/zh-TW.ts b/frontend/src/i18n/locales/zh-TW.ts index 488c1c5a8..544557044 100644 --- a/frontend/src/i18n/locales/zh-TW.ts +++ b/frontend/src/i18n/locales/zh-TW.ts @@ -2848,7 +2848,7 @@ export default { checkEmail: '檢查您的信箱 ({{email}}) 獲取 6 位驗證碼', enterTotpHint: '輸入驗證器 App 中的 6 位驗證碼', accessToken: '存取權杖', - accessTokenHint: '貼上您的拓竹存取權杖(來自 Bambu Studio)', + accessTokenHint: '貼上您的拓竹雲端存取權杖。中國大陸帳號(綁定手機號碼、無 Email 登入)僅能使用此方式登入。可從 MakerWorld 瀏覽器 Cookie 取得權杖,詳見 Wiki。', back: '返回', loginButton: '登入', verifyButton: '驗證',