Commit Graph
100 Commits
Author SHA1 Message Date
bonaminandClaude Opus 5.5 ecfe00307a docs(mqtt-auth): record verified VPS broker setup and ACL isolation test
Checked the live broker on 2026-09-30:
- mosquitto.conf sets no files-backend ACL path. A live test (device A
  subscribing to device B's topics) was denied while A's own topics were
  delivered, so the files backend does not grant-all and every ACL check
  reaches the Console. The missing ACL file is therefore harmless.
- Recorded the container/image, config and passwd locations, which lines
  already match the new code, and the one change still pending
  (auth/acl cache 300s -> 60s before app users go live).
- Added the copy-paste isolation test so it can be re-run after any broker
  config change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 00:57:01 +03:00
bonaminandClaude Opus 5.5 6ea7e9d2c5 docs: MQTT app-user auth reference + CLAUDE.md pointer
docs/mqtt-app-user-auth.md records what future sessions (and whoever builds
the phone app) need and can't get from the code alone:
- app connection contract: username app_<uid>, Firebase ID token as
  password, client id prefix app_<uid>_, TLS only, allowed topics per acc,
- serial field (serial_number, legacy device_id) and the device_serials
  mirror + every code path that must keep it in sync,
- the uid-field lookup rule and ACL cache invalidation,
- legacy "vesper" password flag and its log line,
- rollout checklist: backfill, go-auth VPS config (required/recommended),
  files-ACL check, TLS listener,
- decisions/gaps: FlutterFlow must sync device_serials itself; the
  device_users subcollection is intentionally ignored.

CLAUDE.md gets a short section pointing agents at it before they touch
MQTT auth or anything that edits user_list/status.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 00:45:18 +03:00
bonaminandClaude Opus 5.5 f5db83f26c fix(devices): sync users.device_serials when PUT /api/devices/{id} changes user_list
DeviceUpdate accepts user_list, so a device PUT could add or remove users
without touching their device_serials - leaving the MQTT app ACL stale
(a removed user would keep access; an added user would be denied).

update_device now diffs the old vs new user_list and, in the same atomic
batch as the device write, ArrayUnion/ArrayRemoves the device's serial on
each added/removed user, then invalidates their MQTT ACL cache entries.
Dangling user references are skipped (updating a missing doc would fail
the whole batch). PUTs without user_list take the old single-update path.

Covered by tests/test_device_serials_sync.py (fake Firestore).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 00:44:34 +03:00
bonaminandClaude Opus 5.5 3acd89a2c6 test(mqtt-auth): cover /mqtt/auth/user and /mqtt/auth/acl
First pytest suite in the backend (backend/tests/, run from backend/ with
`python -m pytest tests`). firebase_admin.verify_id_token and Firestore
are mocked, so no network access is needed. 82 cases:

- /user devices: HMAC ok / wrong / other serial's HMAC, kiosk HMAC,
  legacy password with flag on/off, legacy rejected for non-device-shaped
  usernames and for app_ users, HMAC rejected for app_ users, legacy-login
  log rate limiting.
- /user app users: valid token (asserts check_revoked=True), token for a
  different uid, revoked, expired, blocked user, unknown uid, empty uid,
  and that a denied token never appears in logs.
- /acl app users: acc 1/2/4 allow/deny per topic, unsupported acc values,
  wildcards, foreign serial, malformed topics, wrong clientid prefixes
  (incl. uid-prefix collision), blocked/unknown users, cache hit +
  invalidate, cache expiry.
- /acl devices/kiosk/superuser: unchanged behaviour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 00:04:37 +03:00
bonaminandClaude Opus 5.5 81365eed89 feat(mqtt-auth): put legacy "vesper" password behind MQTT_ALLOW_LEGACY_PASSWORD
The shared legacy password is still needed for boards on pre-HMAC
firmware, but it was accepted for any username. Now:
- controlled by MQTT_ALLOW_LEGACY_PASSWORD (config.py, default true;
  documented in .env.example) so it can be switched off without a deploy,
- only accepted for device-shaped usernames (uppercase alphanumeric
  segments joined by "-", optional "-kiosk"), never for app_ users or
  any other shape,
- every successful legacy login is logged at WARNING with the username,
  rate-limited to once per username per hour, so the boards still
  depending on it are visible before the flag is turned off.

HMAC auth is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 00:03:23 +03:00
bonaminandClaude Opus 5.5 1253ec155e feat(mqtt-auth): per-device topic ACL for phone-app users
POST /mqtt/auth/acl now handles "app_<uid>" users:
- topic must be exactly vesper/{serial}/<a>/<b> with serial in the user's
  device_serials (resolved by the users doc `uid` field),
- publish (acc 2): only control/command,
- subscribe (acc 4) and read/delivery (acc 1): only control/ack,
  status/heartbeat, status/playback,
- wildcard topics (+ / #) are denied,
- clientid must start with "app_<uid>_" so one user cannot reuse another
  user's client id to kick them off,
- blocked users are denied; anything else (incl. other acc values) is 403.

Lookups go through mqtt/app_users.py's 60s TTL cache so per-message
checks don't hit Firestore every time; assign/unassign/block invalidate it.

Also fixes the acc comment: mosquitto passes 1 = read (delivery),
2 = write (publish), 4 = subscribe - not "1 = subscribe, 3 = both".
Device/kiosk ACL is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 00:02:48 +03:00
bonaminandClaude Opus 5.5 8a668ca60f feat(mqtt-auth): authenticate phone-app users with Firebase ID tokens
The remote FlutterFlow app connects to Mosquitto as "app_<firebase_uid>"
with a Firebase ID token as the password, so no per-user MQTT accounts
need to exist anywhere.

For app_ usernames, POST /mqtt/auth/user now:
- verifies the token with firebase_admin.auth.verify_id_token
  (check_revoked=True),
- requires the decoded uid to equal the uid in the username,
- requires a users doc with that `uid` field (queried, not by doc id)
  whose status is not "blocked" (same meaning as users.service.block_user).
It returns 200/403 and logs the deny reason - never the token.

app_ usernames never fall through to the HMAC / legacy "vesper" check.
Device and kiosk auth are unchanged. App users are still denied every
topic by the existing ACL until the app ACL lands in the next commit.

Both handlers are now plain `def` so the blocking Firestore / Firebase
calls run in FastAPI's threadpool instead of stalling the event loop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 00:02:21 +03:00
bonaminandClaude Opus 5.5 85bd2f7e51 feat(scripts): idempotent backfill of users.device_serials from devices.user_list
One-off script to populate the new device_serials array for assignments
made before the assign/unassign sync existed. Treats each device's
user_list as the source of truth and sets every user's device_serials to
exactly the matching serials, so re-running it is a no-op.

Dry run by default; --apply writes (batched, <=400 per commit).

It also reports users whose doc ID != uid field (MQTT resolves users by the
uid field), users with no uid, devices with users but no serial, and
user_list entries pointing at non-existent users. user_list entries are
accepted as DocumentReferences, "users/{id}" paths or raw doc IDs, and an
entry that matches a uid field rather than a doc ID is mapped to its doc.

Dry run against current data: 13 devices, 13 users, 11 users to update,
0 doc-ID/uid mismatches, 1 dangling user_list entry
(Cx2Va72sUzDbr1T8Ebbh on BSVSPR-26I047-STD10R-88YFJP).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 00:01:36 +03:00
bonaminandClaude Opus 5.5 79a87e48f1 feat(users): store device_serials on user docs for direct user->device lookup
Adds a `device_serials: [string]` array to Firestore `users` docs so the
MQTT ACL (and get_user_devices) can answer "which boards may this user
reach?" without streaming the entire devices collection.

- The serial is the value used in MQTT topics vesper/{serial}/...: the
  device doc's `serial_number` (flashed into NVS, used by the firmware as
  its MQTT id), falling back to the legacy `device_id` for old docs.
  Centralised in users.service.device_serial_of().
- assign_device / unassign_device now write the device's user_list and the
  user's device_serials (ArrayUnion/ArrayRemove) in one atomic batch.
- The device Manage tab endpoints (POST/DELETE /api/devices/{id}/user-list)
  also edit user_list, so they get the same batched sync - otherwise the
  most common assignment path would silently leave device_serials stale.
- get_user_devices resolves devices via device_serials with chunked
  Firestore "in" queries instead of a full collection scan. Requires the
  backfill script (next commit) to be run for existing assignments.
- New mqtt/app_users.py: resolves users by the `uid` FIELD (not doc id -
  create_user uses .add(), FlutterFlow uses uid as doc id) with a 60s
  in-process TTL cache. Assign/unassign, update, block/unblock and delete
  invalidate that uid's entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 00:00:49 +03:00
bonaminandClaude Opus 5.5 017911bece docs(api-reference): rf.* is agnus-only — disabled on Vesper Standard (firmware F-069)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 23:54:12 +03:00
bonaminandClaude Opus 5.5 c36e4c41f8 docs(api-reference): loading playback state and playback_failed report (firmware F-068)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:25:38 +03:00
bonaminandClaude Opus 5.5 8b229e7fb3 docs(api-reference): list every playback.play/stop reply and error string (firmware F-067)
Replaces the generic "Playback command failed" with the firmware's specific
messages, documents "Already stopped" as SUCCESS, url no longer sticky,
and the stricter field validation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:18:13 +03:00
bonaminandClaude Opus 5.5 9d9b46056a docs(api-reference): status/playback is republished on every MQTT connect (firmware F-066)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:11:20 +03:00
bonaminandClaude Opus 5.5 adec979182 docs(api-reference): document the "stopping" playback state (firmware F-065)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:10:03 +03:00
bonaminandClaude Opus 5.5 99baec9fcf docs(api-reference): document new status/playback fields (firmware F-064)
pid, speed, duration, started_at, source, ts on status/playback (and the
same fields on the WebSocket playback INFO event), plus how to handle a
stale retained message and when the epoch fields are 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:09:16 +03:00
bonaminandClaude Opus 5.5 c56344a6dc docs(api-reference): sync with firmware handler audit — playback, rf namespace, clock/ota/files fixes
Mirrors the firmware repo's api-reference.md audit (project-vesper
docs/reference/api-reference.md):

- playback.play: correct field semantics (url not download_url, speed boot
  default 500 / 0→300, note_assignments value = 1-based bell, sticky fields,
  legacy total_duration ignored without continuous_loop); playback.stop has
  no error path.
- UART whitelist now matches UARTTransport::WHITELIST (5 raw cmd strings).
- Add the missing rf namespace (set_slot/clear_slot/get_slots/enable) with
  every error string.
- Fix clock.set_alerts (camelCase), clock.set_silence (nested), clock.set_time
  offsets, relay.test_bell missing error, ota.update default channel,
  ota.set_channel description, files.download download_url, list_builtin
  "pid" key, logs.list extra error, telemetry boot-history limit=0,
  diagnostics topic, network.set_config AP-mode messages, ping data.type.
- Legacy map now matches LegacyAdapter's tables; message-format card and
  req_id notes reflect MQTT-only req_id and per-transport "v" defaults.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:02:58 +03:00
bonaminandClaude Sonnet 5 0c0dd9d0e9 fix(devices): signal glyph polish, fix invisible copy button, even firmware row
Follow-up on 529e866 after review:

- SignalIndicator: offline now renders a diagonal slash (standard
  "no signal" convention) instead of an X. Added a distinct amber "?"
  state for devices that are online but whose firmware never reports rssi
  at all (legacy v1 heartbeats predate the rssi/state/ok fields — see API
  Reference's v2 migration notes) — previously this looked identical to
  "not loaded yet". DeviceListCardView's online block now opts into this
  by passing isOnline explicitly.

- Copy-to-clipboard for the serial number was invisible: the icon button
  had opacity:0 as an inline style, which beats a CSS hover rule at equal
  specificity, so the reveal-on-hover code paths never fired. Replaced
  with a shared CopyableSerial component (components/shared/) where the
  whole serial text is clickable, not just a trailing icon, and the icon
  sits at partial opacity at rest instead of fully hidden. Wired into both
  DeviceDetail's header and OverviewTab's hero Serial Number field.

- GeneralTab firmware hero: all stat columns are now equal-width via a
  grid instead of ad-hoc flex gaps, and backup_version's "Unknown" sentinel
  (the device's own placeholder when no second OTA slot has ever been
  flashed) is no longer rendered as the literal string "vUnknown".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 12:45:01 +03:00
bonaminandClaude Sonnet 5 529e866fff fix(devices): offline signal glyph, plus firmware UI across Device Detail tabs
SignalIndicator now takes an isOnline prop — when a device is offline it
renders a dimmed amber glyph with an X instead of showing the last-known
(possibly full-bars) RSSI reading, which was misleading on the Device List
table view. Card view already special-cased offline devices and is
unaffected.

Also, while working the device details surface:
- DeviceDetail: hover-to-reveal copy button next to the serial number
  subtitle in the page header.
- OverviewTab hero: swapped the redundant "Location" field (already shown
  in detail on the General tab's map) for live Firmware Version.
- GeneralTab: new full-width, compact Firmware hero row (version, channel,
  validation state, boot count, backup slot) sourced from firmware.status.
- ControlTab: max width brought in line with every other tab (2000, was
  1400), and added a 4th "Firmware" section with channel selection,
  Update Now, custom-URL flash, and commit/rollback controls, wired to the
  firmware.status / ota.* / firmware.commit / firmware.rollback commands
  documented in the API Reference.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 11:47:10 +03:00
bonaminandClaude Sonnet 5 c8ac0b78c5 docs(api-reference): document req_id, rework Transports/Legacy tabs, fix playback.play fields
req_id (added 2026-09-21 in the v2 topic rebuild, F-062):
- New Extras tab documents it fully: envelope-level (sibling of cmd,
  not inside contents), always optional, exact echo/reply behavior,
  parse-failure edge case, and an MQTT-only gap — WebSocket and HTTP
  transports don't actually read or echo it despite CommandBus
  supporting it generically at the bus level.
- Every command's Contents section now carries a persistent note
  pointing to Extras, instead of duplicating the explanation 40+ times
  or only mentioning it in one Transports card.
- Removed the old "req_id Correlation" card from the Transports tab —
  superseded by the Extras tab.

Transports tab: dropped the V2/Legacy sub-tab switcher. It only ever
showed a "not documented yet" placeholder for Legacy, and the legacy
topic set belongs with the rest of the migration reference, not
alongside the current transport list.

Legacy Migration tab (renamed from "v1 → v2 Migration"): added a
"Legacy Topic Migration" table ahead of the command migration table,
mapping every MQTT topic from the old pre-rewrite firmware
("Controller - Production FW") to its v2 equivalent — including three
v2 topics (system/alerts, system/info, system/metrics) that have no
legacy predecessor at all. Reconstructed from that firmware's source;
it has been fully replaced, so this is historical reference only.

playback.play: filled in the full contents field set per the current
Player.cpp implementation — segment_duration, pause_duration,
total_duration, and continuous_loop are the legacy (no "mode") path,
while duration is the v2 path read only when mode is present. Added a
warning callout since sending mode alongside the legacy duration
fields doesn't merge behavior — only one path is read, based solely on
whether mode is present. This is intentional: mode is how a v2 caller
opts in, and its absence is how a v1 caller's request still works.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 20:35:20 +03:00
bonaminandClaude Sonnet 5 80c0e955f0 fix(devices): zero out unassigned bell outputs when saving Bell Mechanisms
relay.set_config is a partial update on the firmware side — any bell
index not present in the durations/outputs maps is left completely
untouched. EditBellOutputsModal and OnboardDevice's bell config step
both only sent entries for the currently-active bell count, so
deselecting a bell (lowering the active count) never actually
deactivated its output on the device — it stayed wired to whatever it
was last set to. The only way to disable a bell was to reselect it and
explicitly set it to "Disabled", which isn't obvious.

Both now send all maxOutputs slots on every save, forcing output to 0
for anything beyond the active count, so lowering the active bell
count reaches the firmware the way it visually appears to in the UI.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 20:35:03 +03:00
bonaminandClaude Sonnet 5 ae5da6046f docs(CLAUDE): require every change to be documented and committed
Adds a Documentation & Commit Policy section and a Golden Rules entry
requiring every change, however small, to be committed with a
descriptive message explaining why — so project history stays a
reliable record of what happened over time.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 18:36:56 +03:00
bonaminandClaude Sonnet 5 aad5f4b98c chore(strategies): add Health Monitor standalone design mockup
Throwaway HTML prototype for the device Health tab's layout/visual
direction, kept alongside the other planning docs in strategies/.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 18:36:50 +03:00
bonaminandClaude Sonnet 5 751cac72ce fix(docker): move postgres data to a named volume, off the WSL2 bind mount
The ./data/postgres bind mount hit a WSL2/Docker Desktop bug where the
9p/virtiofs bridge reports normal postgres:postgres 0600 ownership but
the kernel still refuses the postgres user's own open() calls for
write — silent on read, fatal on any WAL write, which took the whole
database down after an unclean shutdown. Switches to a Docker-managed
named volume (bellsystems-postgres-data) that lives inside the WSL2
VM's own filesystem instead. The old bind-mounted data is left in
place at ./data/postgres/ as an untouched backup — that folder was
too wedged by the same bug to even rename, so it's abandoned rather
than deleted.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 18:36:28 +03:00
bonaminandClaude Sonnet 5 cef5e1f0f5 chore(docker): restart backend, frontend, and nginx unless stopped
Matches postgres, which already had restart: unless-stopped — the app
containers were the only ones that didn't come back automatically
after a host reboot or Docker Desktop restart.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 18:36:20 +03:00
bonaminandClaude Sonnet 5 f036000769 feat(manufacturing): extract shared useSerialConnection hook for provisioning wizard
WebSerial port ownership was previously held inside StepFlash, which
made it awkward for other steps (StepVerify) to read from the same
port without fighting over exclusive access. useSerialConnection
centralizes port open/close/read/write so the wizard's steps share one
connection lifecycle instead of each managing their own.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 18:35:31 +03:00
bonaminandClaude Sonnet 5 d87925516a feat(devices): add ResetStatsRequest/ResetStatsResult models
Missed from the previous Reset Stats commit — these are the
request/response schemas for POST /devices/{id}/reset-stats.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 18:35:20 +03:00
bonaminandClaude Sonnet 5 f54500a9d9 feat(devices): add Reset Stats action for clearing pre-ship QA/bench data
New POST /devices/{id}/reset-stats + ResetStatsModal lets staff clear
QA/bench test data accumulated before a device ships to a customer:
Firestore bell/playback counters and Postgres history tables (logs,
heartbeats, commands, boot events, alert events, ping samples,
diagnostics reports, control/reports, and opt-in live alerts). The two
firmware-side resets (telemetry.reset_boot_data, logs.clear) go over
the existing MQTT command/ack flow instead, since they need the device
online and duplicating that round-trip server-side would just be a
second, inferior implementation.

Also includes two incidental cleanups in devices/router.py: audit-log
entries for create/update now use console_name in their label (missed
by the earlier console_name commit), and add/remove-device-user rename
their local Firestore client from `db` to `fs` to stop shadowing the
`db: AsyncSession` dependency param in the same function scope.
AddDeviceUserModal's results list also gets a max-height + scroll so a
long match list doesn't grow the modal off-screen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 18:35:01 +03:00
bonaminandClaude Sonnet 5 4737ab4306 feat(devices): add device onboarding wizard
New guided flow (Bell Cloud > Devices > Onboard) for claiming a
manufactured/flashed device into a customer's fleet in one pass:
looks up the device by serial, finds or creates the customer
(CreateCustomerModal for a quick inline create), finds or creates the
app user account (reuses CreateUserModal from the user-creation work),
and assigns the device — replacing what used to require jumping
between the inventory, CRM, and user-management pages separately.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 18:34:32 +03:00
bonaminandClaude Sonnet 5 6686faad6b feat(manufacturing): enrich inventory list with customer/user details and filters
The inventory list and detail page previously only showed raw
customer_id / user_list references. list_devices now batch-resolves
customer names and user display names/emails (via Firestore get_all(),
avoiding an N+1 round trip per device) and exposes them as
customer_name / users on DeviceInventoryItem. Search now matches
against device name, console name, customer name, and assigned users
in addition to serial/owner/batch.

Also adds hw_types (multi-select board type) and has_users filters to
GET /manufacturing/devices, and carries console_name through for
display. DeviceInventoryDetail is updated to show and use all of this
(customer name, assigned users, new UI components for signal/charts).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 18:34:08 +03:00
bonaminandClaude Sonnet 5 b07bdafd85 fix(header): resolve breadcrumb labels for devices and melodies correctly
- devices: breadcrumb now falls back through console_name -> device_name
  -> device_id, matching every other device display label in the console
- melodies/archetypes: breadcrumb was reading a bare d.name field that
  doesn't exist on the melody schema (name lives at
  d.information.name.<locale>), so these breadcrumbs always showed blank.
  Now uses getLocalizedValue like the rest of the melodies UI.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 18:33:55 +03:00
bonaminandClaude Sonnet 5 dff1a35560 feat(settings): add log retention settings page
Sysadmin/admin-only settings page for configuring how long device log
history is kept before pruning. GET/PUT /api/settings/log-retention,
new LogRetentionSettings page, nav entry, and route.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 18:33:40 +03:00
bonaminandClaude Sonnet 5 acd1896788 feat(devices): add Health tab, log explorer, and fleet health indicators
Surfaces the new device-health telemetry (boot events, ping RTT,
diagnostics reports, alert events) across the console:

- HealthTab: boot/diagnostics timeline, CPU temp and RSSI charts
  (LineChart), current alert status, and a Settings sub-tab for
  DeviceHealthSettings thresholds
- LogsTab: dedicated log explorer embedded in the Health tab, with
  level/source filtering and fmtLogTimestamp for dense timestamp rows
- OverviewTab: a "Latest Device Issue" card showing the most recent
  alert event, colour-coded by severity and fading with age, plus a
  modal to inspect the surrounding log lines
- DeviceList/DeviceListCardView/DeviceListMapView: fleet list gains a
  SignalIndicator-based RSSI display in place of the plain online dot

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 18:32:06 +03:00
bonaminandClaude Sonnet 5 0d79a9f72c feat(users): support creating app users with a Firebase Auth account
New users can now be created with a password, which creates a real
Firebase Auth account (so they can log into the mobile app immediately)
alongside the Firestore profile document. UserCreate is now
UserProfile + password (request-only, never persisted or echoed back);
deleting a user also removes their Auth account.

- backend/users: split UserCreate into UserProfile (persisted shape)
  and UserCreate (adds password), wire firebase_auth create/delete
- CreateUserModal: new lightweight modal for creating a user from
  other flows (e.g. device onboarding) without leaving the page
- UserForm: adds the password field for new users; also fixes
  useToast() being used undestructured (toast.success(...) was being
  called on the hook's return value instead of its .toast method)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 18:31:37 +03:00
bonaminandClaude Sonnet 5 6e0a291228 feat(devices): add console-only display name (console_name)
Lets staff set a friendly name for a device independent of whatever
name the end user gave it in the app. console_name is never shown to
app users and never synced from/to device_name; every display label
across search, equipment/helpdesk name resolution, device search, and
the Manage tab's issue linker now falls back through console_name ->
device_name -> serial rather than device_name alone.

Also includes an incidental one-line fix in devices/service.py: the
nested-struct deep-merge in update_device() was missing the newly
added device_health_settings key.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 18:31:10 +03:00
bonaminandClaude Sonnet 5 8ec13947cb fix(devices): restore warranty/maintenance dates missing from timestamp field list
subscrStart, warrantyStart, and maintainedOn are written to Firestore as
Timestamps like the other date fields here, but were missing from
_TIMESTAMP_FIELD_NAMES, so they weren't being converted back to ISO
strings on read.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 18:30:15 +03:00
bonaminandClaude Sonnet 5 0bec4c4648 feat(devices): add DeviceHealthSettings model for per-device alert thresholds
Two-tier (warning/critical) threshold config per device — restarts/week,
RSSI floor, free-heap floor, CPU temp ceiling, plus an offline timeout
used for both a client-computed health status icon and the Health tab
chart's gap detection. Purely advisory for now: no server-side
email/push alerting infra exists yet, so email_on_threshold and
push_on_crash_boot are placeholders for that future work.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 18:27:35 +03:00
bonaminandClaude Sonnet 5 cc138e3d82 feat(devices): push General/Clock/Bells/Alerts config live over MQTT
Adopts useDeviceCommand across DeviceDetail and its tabs so config
changes (log levels, clock settings, bell outputs, alert thresholds,
backlight, attributes) are sent to the device immediately via
control commands and only persisted to Firestore once the device
acks success, instead of writing Firestore first and hoping the
device eventually picks it up.

- GeneralTab: log-level sliders now call log.set_serial/sd/mqtt
  directly and revert on failure; a background log.get_config +
  network.info pull reconciles Firestore against the device's actual
  state once per mount
- ClockTab, BellsTab, ControlTab, and the Edit* modals: same
  live-command-then-persist pattern
- EditLoggingModal is removed — its job (log level editing) moved
  inline into GeneralTab's sliders, so a modal round-trip is no
  longer needed
- DeviceDetail wires the shared useDeviceCommand connection through
  to each tab and adds a Health tab entry

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 18:25:30 +03:00
bonaminandClaude Sonnet 5 7c533b9245 feat(mqtt): add device health telemetry and migrate to v2 topic spec
Two efforts that landed together because the v2 topic work extends
tables the health-telemetry effort added days earlier in the same
files/functions, making them impractical to separate cleanly:

Health/diagnostics telemetry (schema, Jul 13-17):
- New Postgres tables: device_alert_events, device_boot_events,
  device_ping_samples, device_diagnostics_reports, plus a `source`
  column on device_logs to distinguish log origins
- Query/service layer in pg_mqtt.py and database/__init__.py for
  inserting and listing this history, plus a "latest metrics" endpoint
  combining most-recent diagnostics + ping RTT per device
- mqtt/router.py gains list endpoints for alert/boot/ping/diagnostics
  history, consumed by the upcoming Health tab

MQTT v2 topic migration (Sep 21):
- Heartbeat payload flattened per vesper_mqtt_topic_spec_v2.md, adding
  rssi/free_heap/state/ok fields
- Command replies move to control/ack, device-initiated events to
  control/reports; mqtt/client.py subscribes to the new topic set and
  runs a ping_loop (wired up in main.py) for RTT sampling
- mqtt/logger.py and pg_mqtt.py updated to parse and persist the new
  payload shape alongside the legacy fields for backwards compatibility

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 18:24:36 +03:00
bonaminandClaude Sonnet 5 e5d556fee1 feat(devices): extract useDeviceCommand hook for shared MQTT command/ack flow
Pulls the send-command-and-await-ack machinery out of DeviceDetail.jsx
into a reusable hook, so other pages (the onboarding wizard, etc.) can
send a device a command and await its control/ack reply the same way,
with a live-updating toast for non-silent commands.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 18:22:48 +03:00
bonaminandClaude Sonnet 5 38750ba951 fix(scrollbar): theme scrollbars inside portal-rendered elements
Scrollbar theming was scoped to .app, so anything rendered outside it via
createPortal(..., document.body) — Modal, Select/MultiSelect's floating
menu, DataTable's column-visibility picker — fell back to the browser's
default light-on-dark scrollbar. Extends the same scrollbar-color rules
to .modal, .select-menu, and .dt-col-picker.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 18:22:30 +03:00
bonaminandClaude Sonnet 5 7f6145ca35 fix(header): match header glass tint to sidebar, add drop shadow
.header used a lighter 0.30 background tint than .sidebar's 0.40, so
scrolled content underneath showed through almost undimmed even though
backdrop-filter was applied — the two fixed chrome surfaces no longer
read as one consistent glass layer. Aligns the tint and adds a shadow
to give the header separation from content.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 18:22:23 +03:00
bonaminandClaude Sonnet 5 2f7c5e859d feat(ui): add MultiSelect, LineChart, SignalIndicator, PressHoldButton, EditableText, TimeRangeSelect
Six new design-system components, all documented with live examples in
the StyleGuide as required by CLAUDE.md before any page can use them:

- MultiSelect: checkbox dropdown for filters/tags, built on Select's
  trigger/menu styling
- LineChart: telemetry/time-series charting for the upcoming Health tab
- SignalIndicator: signal-strength glyph for device RSSI display
- PressHoldButton: press-and-hold confirmation for destructive actions
- EditableText: inline click-to-edit text with a hover/focus-revealed
  pencil affordance
- TimeRangeSelect: preset + custom time-range picker, backed by the new
  lib/timeRange.js helper

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 18:22:02 +03:00
bonaminandClaude Sonnet 5 883c0556dd feat(ui): add pending/update toast state and shared floating-menu positioning
Toast gains a pending/update lifecycle so async actions can show a single
toast that transitions from in-progress to success/error, instead of
firing a new one. Select's floating-menu placement logic is extracted
into a shared helper so other dropdown-style components (MultiSelect)
can reuse it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 18:20:26 +03:00
bonaminandClaude Sonnet 5 1ce0137ba9 feat(mqtt): implement useMqttWebSocket hook
Was previously a dead stub (TODO: implement). This real implementation
is the foundation for live heartbeats, ping RTT, and the device command
ack flow that later commits build on.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 18:20:15 +03:00
bonaminandClaude Sonnet 5 eaf92f1581 docs(api-reference): add MQTT v2 topic spec, split Transports into V2/Legacy
Firmware rebuilt its MQTT topics to v2 (control/command+ack+reports,
status/playback, system/alerts+info+logs+metrics, LWT, req_id, per-topic
switches — see project-vesper's docs/reference/vesper_mqtt_topic_spec_v2.md
and feature-catalog.md F-062). Mirrors that in the console's API Reference:

- Transports tab now splits into V2 (fully documented: topic table with
  per-topic switches, req_id correlation, heartbeat/playback/reports/
  alerts/boot-report/metrics payload cards) and Legacy (placeholder,
  to be filled in later)
- mqtt namespace gains mqtt.get_topics / mqtt.set_topics command docs
- mqtt.disable description updated for its new graceful-offline behavior
- Message envelope docs (top of page) note the new optional req_id field

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 10:49:02 +03:00
bonaminandClaude Sonnet 5 cc28ed880b docs(ApiReferencePage): document system.set_identity console command
Firmware side added an MQTT-only sysadmin command to override a
device's factory-set identity (serial/hw_family/hw_revision) — see
project-vesper commit 64a8bc2. Per docs/README.md's console-sync
rule, mirrors the same command into the console's live API Reference
page.

Note: this repo had substantial other pending uncommitted work
(diagnostics reports, boot history, log retention, etc.) in this
same file and elsewhere at the time of this commit — left untouched
and still uncommitted, only the system.set_identity hunk is included
here.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 20:11:26 +03:00
bonamin 44609352b2 docs(ApiReference): document logs.list/logs.download and extended firmware.status
Mirrors firmware changes: new SD log retrieval commands on LoggingHandler
(logs.list, logs.download — chunked/paginated for offline-device
troubleshooting), and firmware.status now returns OTA rollback safety-net
diagnostics (retry/failure counts, backup partition info).
2026-07-15 00:06:26 +03:00
bonaminandClaude Sonnet 4.6 70e0f9f734 docs(api-reference): add heartbeat payload card with rssi field
Adds a dedicated Heartbeat Payload card to the Transports tab showing
the full JSON schema with per-field descriptions, including the new rssi field.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-13 02:14:53 +03:00
bonaminandClaude Sonnet 4.6 a9b3b4e9b0 feat(ApiReference): redesign network.info and network.status documentation
network.info: updated description and response example to reflect new fields
(subnet, mac, hostname, ssid, connection_type). Notes it as the command to use
for console panel population.

network.status: updated to show slimmed response (connected, ap_mode, rssi,
uptime_ms, state). Notes it as the command for monitoring/polling, not display.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-13 00:53:42 +03:00
bonaminandClaude Sonnet 4.6 65ce446e44 fix(ApiReference): clock.get_config — add timezone fields, fix backlight/bell docs
- clock.get_config response example now includes gmt_offset_sec, dst_offset_sec, ntp_server
- Description updated to note it supersedes clock.get_timezone
- clock.set_backlight field names corrected to match wire format
  (backlight, backlight_output, backlight_on, backlight_off)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-12 19:38:21 +03:00
bonaminandClaude Sonnet 4.6 bff90965bf feat(ApiReference): add clock.set_config, clock.set_alerts_config, relay.set_config entries
Document three new batch commands with full response/errors/example fields:
- relay.set_config: combined bell durations + outputs
- clock.set_config: clock hardware settings (enabled, c1, c2, timings)
- clock.set_alerts_config: alert type, bell assignments, silence windows

Also updated output field notes throughout clock and relay commands to reflect
the enforced 1-based output numbering convention (0 = disabled, 255 = unconfigured).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-12 18:45:12 +03:00
bonaminandClaude Sonnet 4.6 3394716f8f docs(ApiReferencePage): full v2 API reference update
Fixes and additions across all namespaces to accurately reflect firmware v2.1.0:

Fixes:
- system.get_settings → system.get_config (backward compat alias noted)
- network.status returns now includes ssid, mac, hostname
- clock.set_alerts: correct alert_type values (OFF|SINGLE|HOURS, not none|hour|quarter)
- ota: removed non-existent ota.status and ota.check commands
- telemetry.get_loads returns now includes max_loads and guard_enabled

Additions:
- relay.test_bell and relay.test_batch (F-038)
- clock.test_c1 and clock.test_c2 (F-039)
- system.get_config, system.get_all, system.get_telemetry (F-035)
- telemetry.set_max_loads, telemetry.set_guard_enabled, telemetry.get_metrics, telemetry.reset_metrics (F-030, F-031, F-034)
- log.get_config and mqtt.get_config (F-036)
- ota.set_channel
- bells namespace: bells.enable, bells.disable, bells.get_config (F-029, F-036)
- UART whitelist updated for new commands
- Legacy map updated: get_full_settings → system.get_config

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-10 19:55:08 +03:00
bonaminandClaude Sonnet 4.6 abf3c3825b docs(ApiReference): align V2 command reference with actual firmware
playback namespace: replaced stale root-level 'playback' command with
proper playback.play / playback.stop split. playback.play now documents
all firmware attributes: pid, uid, name, url, speed, note_assignments,
mode (single/timed/interval/infinite), duration, pause_duration,
total_duration.

clock namespace: filled in previously empty contents for set_timings
(pulse_duration, pause_duration), set_alerts (alert_type, alert_interval,
hour/half/quarter_bell outputs), set_backlight (backlight, backlight_output,
backlight_on/off), set_silence (daytime and nighttime windows with HH:MM
strings). Fixed clock.get_timezone returns (ntp_server field, removed
non-existent timezone_name). Added exact returns shape for clock.get_face
and full field list for clock.get_config matching handler output.

system namespace: added exact returns shapes for system.status
(player_status, time_elapsed_ms, projected_run_time, strike_counters)
and system.health (critical_count, warning_count, firmware_stable,
subsystems array).

Legacy map: updated playback row to show play→playback.play and
stop→playback.stop. UART whitelist updated to playback.play/stop.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-09 10:03:52 +03:00
bonaminandClaude Sonnet 5 1ed5012a95 fix: key melody binary storage on pid instead of uid, add migration script
melody.uid is never actually populated anywhere in MelodyForm.jsx, so keying
local .bsm storage on it (as the previous commit did) would silently break
for every existing melody. pid is the correct key anyway: it identifies the
underlying archetype binary, and multiple melodies legitimately share one
pid (each remaps the same note sequence to different bells/speed/duration
via its own settings). Deletion is now share-aware — a melody's binary is
only removed from disk once no other melody still references its pid.

Also adds backend/scripts/migrate_melody_binaries_to_local.py to backfill
existing melodies from their old Firebase URLs to local storage, with
--dry-run support and a warning list for pids whose melodies point at
different source files (a pre-existing data issue, flagged for manual
review via each melody's playback button rather than silently resolved).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-06 10:53:25 +03:00
bonaminandClaude Sonnet 5 72f7e00990 feat: serve melody .bsm binaries over plain HTTP instead of Firebase Storage
ESP32 devices can't spare the 40KB+ RAM a TLS client needs, so Firebase
Storage's HTTPS-only download URLs were blocking melody downloads. Binaries
are now written to local disk (./data/melody_binaries) and served through a
new unauthenticated /api/melodies/download/{pid} route, exposed publicly on
a separate melodies.bellsystems.net vhost (plain HTTP, no TLS) so the main
console domain can stay HTTPS-only with no exceptions. Preview audio still
uses Firebase Storage since it's only ever fetched by the HTTPS admin UI.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-06 09:41:24 +03:00
bonamin 9df80dd4e1 fix: vite server hotfix 2026-06-16 12:15:25 +03:00
bonaminandClaude Sonnet 4.6 1022b7e5f1 fix: remove duplicate port mapping 8001:5174 from frontend service
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 19:22:14 +03:00
bonaminandClaude Sonnet 4.6 024ba88470 fix: route manufacturing audit logs to shared Postgres audit log
- Manufacturing router now uses shared/audit.log_action (Postgres) instead
  of the separate manufacturing/audit.py (SQLite mfg_audit_log), so all
  manufacturing events appear in the Log Viewer
- Added log_action calls to 5 previously unlogged endpoints: lifecycle
  patch, lifecycle create, lifecycle delete, flash asset upload, flash
  asset note
- Removed the now-redundant /manufacturing/audit-log endpoint
- Log Viewer restricted to sysadmin only: backend uses require_sysadmin
  (was require_admin_or_above), frontend adds role guard on the page
- Fixed Action badge column clipping: table-layout auto + whiteSpace nowrap
  so the column sizes to fit the widest badge (Status Change)
- Added device_batch entity type to Log Viewer entity labels and filters

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 19:21:39 +03:00
bonamin 9a213e93f8 fix: brought back postgress that we removed in the last commit, and added persistent folder for it 2026-04-19 20:10:40 +03:00
bonamin d8b0b9ce28 fix: Flashing window size made fixed, and fixed Vite.config.js 2026-04-19 15:52:34 +03:00
bonamin 063106a29c Reverted back to the original docker-compose.yml file. 2026-04-19 15:46:07 +03:00
bonamin 6a958a8d7d update: Add Global Search on Header, Add Global Audit log for all actions. 2026-04-19 15:41:29 +03:00
bonamin 4f35bef6e3 fix: login issue #2 2026-04-17 16:04:12 +03:00
bonamin 2ef199e4c5 fix: login issue 2026-04-17 16:01:50 +03:00
bonamin a605143c5d Phase 5 of Migration 2026-04-17 15:51:27 +03:00
bonamin da4608c937 Phase 4 of Migration 2026-04-17 15:44:17 +03:00
bonamin 83361fad77 Phase 3 of Migration 2026-04-17 15:39:29 +03:00
bonamin c7d5206d0c fix: deduplicate order_number collisions during Firestore orders migration 2026-04-17 15:30:45 +03:00
bonamin 914027e580 fix: use full doc.id as fallback order_number to avoid unique constraint collision 2026-04-17 15:28:28 +03:00
bonamin b70753d805 Phase 2 of Migration 2026-04-17 15:25:58 +03:00
bonamin a7b73b0564 fix: move SET LOCAL inside transaction in quotation/media/comms migration scripts 2026-04-17 15:15:43 +03:00
bonamin 4c2400b596 Phase 1 of Migration. Running Scripts 2026-04-17 15:11:12 +03:00
bonamin 0a8a42d69b Initial Switch to V2. Completely Overhauled Backend, Frontend and General Structure. 2026-04-17 14:45:30 +03:00
bonamin eb773c5531 fix: added ACL fix script 2026-04-03 18:08:07 +03:00
bonamin ea8b2c96d6 feature: added archetype migration script 2026-04-03 17:46:39 +03:00
bonamin 435aa88e29 update: Added asset upload for bespoke boards 2026-03-31 18:01:32 +03:00
bonamin 7a5321c097 update: Added NVS Gen on the Flasher 2026-03-27 11:17:10 +02:00
bonaminandClaude Sonnet 4.6 2b05ff8b02 feat: CRM customer/order UI overhaul
Orders:
- Auto-set customer status to ACTIVE when creating a new order (both "+ New Order" and "Init Negotiations")
- Update Status panel now resets datetime to current time each time it opens
- Empty note on status update saves as empty string instead of falling back to previous note
- Default note pre-filled per status type when Update Status panel opens or status changes
- Timeline items now show verbose date/time ("25 March 2026, 4:49 pm") with muted updated-by indicator

CustomerDetail:
- Reordered tabs: Overview | Communication | Quotations | Orders | Finance | Files & Media | Devices | Support
- Renamed "Financials" tab to "Finance"

CustomerList:
- Location column shows city only, falls back to country if city is empty

OverviewTab:
- Hero status container redesigned: icon + status name + verbose description + shimmer border
- Issues, Support, Orders shown as matching hero cards on the same row (status flex-grows to fill space)
- All four cards share identical height, padding, and animated shimmer border effect
- Stat card borders use muted opacity to stay visually consistent with the status card

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 20:21:10 +02:00
bonaminandClaude Sonnet 4.6 5d8ef96d4c update: CRM customers, orders, device detail, and status system changes
- CustomerList, CustomerForm, CustomerDetail: various updates
- Orders: removed OrderDetail and OrderForm, updated OrderList and index
- DeviceDetail: updates
- index.css: added new styles
- CRM_STATUS_SYSTEM_PLAN.md: new planning document
- Added customer-status assets and CustomerDetail subfolder

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 10:39:38 +02:00
bonaminandClaude Sonnet 4.6 fee686a9f3 feat: add Serial Monitor public page and Log Viewer settings page
- New public page at /serial-monitor: connects to Web Serial (115200 baud),
  streams live output, saves sessions to localStorage + downloads .txt
- New protected page at /settings/serial-logs (admin/sysadmin only):
  lists saved sessions, expandable with full scrollable log, search,
  export and delete per session
- Registered routes in App.jsx and added Log Viewer to Console Settings sidebar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 10:39:32 +02:00
bonamin b2d1e2bdc4 feature: Added Transactions and major Order System Overhaul 2026-03-25 10:32:47 +02:00
bonamin 2d57c75d2f fix: Cloudflash now supports full-wipe before flashing 2026-03-20 08:40:20 +02:00
bonamin d8ba64da55 fix: NVS Generator. cosmetic: Changed CloudFlash page a bit 2026-03-19 21:01:17 +02:00
bonamin 29bbaead86 update: added assets manager and extra nvs settings on cloudflash 2026-03-19 11:11:29 +02:00
bonamin d0ac4f1d91 update: overhauled firmware ui. Added public flash page. 2026-03-18 17:49:40 +02:00
bonamin 4381a6681d update: firmware and provisioning now supports bootloader and partition tables 2026-03-16 08:52:58 +02:00
bonamin 360725c93f fix: row-alt background now works on customers list too 2026-03-14 11:17:58 +02:00
bonamin dd607a04a1 ui: added subtle tint for alternating rows on tables 2026-03-14 11:09:32 +02:00
bonamin 15c419b7bf fix: devices list accepts ips as single strings / various ui changes 2026-03-14 10:58:25 +02:00
bonamin 6f9fd5cba3 fix: Bugs created after the overhaul, performance and layout fixes 2026-03-08 22:30:56 +02:00
bonaminandClaude Sonnet 4.6 8c15c932b6 chore: untrack .claude/ folder and update .gitignore
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 11:39:42 +02:00
bonamin c62188fda6 update: Major Overhaul to all subsystems 2026-03-07 11:36:46 +02:00
bonamin 810e81b323 fix: Various fixes. Mail, UI, Flash etc 2026-02-27 14:32:24 +02:00
bonamin 7585e43b52 style: Updated the overall UI of the provisining pages 2026-02-27 12:23:17 +02:00
bonamin 47570257bd fix: Verification in provisioning fixed 2026-02-27 11:15:39 +02:00
bonamin 7f51c60062 fix: Trying to fix Auto Restart. And Fixed MQTT admin auth 2026-02-27 10:17:38 +02:00
bonamin 12784462f9 fix: Different approach on the COM Port and MQTT Fix 2026-02-27 09:57:49 +02:00
bonamin 1a5e448e4e fix: Release COM port after flashing and then Reconnect to get Serial Logs 2026-02-27 09:44:26 +02:00
bonamin 5c6c871bb6 fix: Add a wait after the flash to the Provisioning tab 2026-02-27 09:39:06 +02:00
bonaminandClaude Sonnet 4.6 4ea8e56485 fix: configure gitea webhook auto-deploy, fix NVS CRC, and improve flash UI
- Add deploy-host.sh for webhook-triggered docker redeploy
- Update docker-compose.yml and nginx.conf for auto-pull setup
- Fix vite.config.js and admin router for deployment environment
- Fix NVS CRC seed to use 0xFFFFFFFF to match esp_rom_crc32_le
- Add dual-panel flash UI: esptool log + live 115200 serial monitor
- Auto-reset device via RTS after flash (no manual power cycle needed)
- Clean up Header.jsx debug title text

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 09:19:07 +02:00