Charging dashboard (Slack TUI) + reconcile main→dev keeping Constellation#71
Merged
Conversation
Update status page to visually pulse the newest column when the health mosaic advances and add a unit test for the behavior.
DBC and Dynamic y axis selection
Overlay speed calculation updated to derive vehicle speed from wheel RPM using gear ratio and wheel radius (replaces naive rpm/500). Telemetry-store rpm/motor_speed signal aliases switched to INV_Fast_Motor_Speed. universal-telemetry-software: add missing_seq_seconds to track when gaps occurred, clear on sequence reset, cap removal of oldest missing entries, record recovery timestamps and use them to mark per-second status correctly, and use a robust TCP read loop that accumulates all chunks.
…newest season table
Update docs
Add a Cloudflare Wrangler/Durable-Object relay backend under flight-recorder/relay-worker (package.json, src/index.ts, tsconfig.json, wrangler.toml) and a README describing deployment and usage as an optional public live-relay for remote viewers. Update flight-recorder documentation and top-level README to mark the app as internal (protected by Cloudflare Zero Trust), clarify store-and-forward behavior, relationship to the old lte-relay approach, optional live-relay semantics (public/tokenless worker, 1 Hz synthetic heartbeat, demo-source behavior), and DBC access/build notes. Also add a LiveRelayService and update WebSocketService and App.tsx to expose guarded controls for WS Relay vs DB forward so live relay can be used independently of database sync.
TelemetryHandler: treat both UTS (1999) and flight-relay (0x7FD) CAN IDs as diagnostic heartbeats and ingest them under DIAG_MSG_IDS.HEARTBEAT. RadioStatChips now uses the DIAG_MSG_IDS constant instead of a hardcoded '1999' string. Added TelemetryHandler tests to assert UTS and relay heartbeat frames are routed to the heartbeat diagnostic message. LiveRelayService: strip search and hash when building the /session URL to avoid stale query/hash data; updated its test to stub global fetch and ensure sessions can be created from older ingest URLs. Misc: test cleanup unstubs globals after each run.
Integrate Flight Recorder relay workflow
Introduce a unified system health endpoint and UI backed by an atomic health snapshot. - README and MACBOOK_DEPLOY: document the new /health endpoint and troubleshooting steps. - src/data.py: add HEALTH_FILE env var; compute detailed health snapshot (components, timescale, redis, UDP listener, car state, version, clock, stats); perform Redis ping and timescale parsing; record uptime and UDP listener bound state; write snapshot atomically to HEALTH_FILE. - src/status_server.py: add /health handler, helpers to load, finalize and synthesize unknown/stale health payloads, and mark stale snapshots when producer_ts is old; introduce HEALTH_STALE_SECONDS config. - status/index.html: replace pipeline WebSocket-driven status with polling /health, add system health UI (CSS/JS) and rendering logic, update metrics/status text and polling cadence. - tests/test_status_server.py: add tests for loading valid, missing, malformed and stale health snapshots and retain 404 test for unknown endpoints. This change enables a simple JSON health probe for monitoring and makes the status page reflect aggregated infrastructure state even when the car is powered off.
Switch MacBook deployment to use the committed deploy/.env.macbook via --env-file and make TimescaleDB, media, and cloudflared optional Docker Compose profiles. Add ENABLE_TIMESCALE_LOGGING (default false) to the env file and update docker-compose.macbook-base.yml to honor the env file and include profiles for timescale/media/tunnel. Update docs (top-level README, universal-telemetry-software/README.md, MACBOOK_DEPLOY.md, WHICH_ONE.md, and offline README) to describe the new minimal default stack, show example profile commands, and note which images are optional for offline saves.
Introduce an "auto" mode for ENABLE_TIMESCALE_LOGGING so the service will probe the configured POSTGRES_DSN and enable the Timescale writer only when the DB is reachable.
…version before notifyAll so subscribers see correct version. (#68)
Track WebSocket bridge client counts through Redis as total, internal, and external clients. Treat loopback connections such as the local relay upstream as internal so the status page does not count them as dashboard viewers. Refresh the count periodically while the bridge is running, preventing the health snapshot from falling back to "Client count unavailable" after the Redis key expires. Update the status UI to show external/internal counts and add focused regression coverage.
Add a new GitHub Actions job (macbook-lan-sender) to smoke-test the MacBook one-click stack using deploy/docker-compose.macbook-base.yml. The job loads built Docker images, pulls base images, sets up Python, brings up the MacBook stack, starts lan_sender inside the telemetry container, runs pytest against a new test file, and collects/logs artifacts on failure. Also add tests/universal-telemetry-software/tests/test_macbook_base_lan_sender.py which exercise container health, status page, PECAN, telemetry logs, WebSocket CAN messages, and health metrics. Update CI compose validation to include the macbook compose file and add the new job as a dependency for publish-telemetry.
Add full Linux support and an interactive --hotspot mode to the one-line base-station installer; keep macOS support. The installer now bootstraps git and Docker on Debian/Ubuntu/Raspberry Pi OS, can enable a NetworkManager-based Wi‑Fi AP (wfr-hotspot) and installs a systemd unit for it. CI workflow updated to validate compose from the repo root and run a linux e2e job that syntax-checks the script, validates compose, runs the installer, asserts core containers, checks service ports, verifies idempotency, and ensures non-interactive hotspot behaviour. Documentation (README and MACBOOK_DEPLOY.md) and WHICH_ONE.md updated with Linux/RPi instructions and hotspot notes. Fix .env macbook DBC_HOST_PATH to be repo-root relative and add wfr-hotspot.service file.
Adds !stats command and a 9 AM ET daily auto-post showing rows logged, CAN message count, and testing duration for yesterday and the past 7 days. Pulls from TimescaleDB wfr26 and monitoring tables directly. Also adds psycopg2-binary to slackbot requirements.
This reverts commit 4621ab3.
…-branch slicks API
- Use generic placeholders (<season_table>, <signal_1>, etc.) instead of
team-specific table and signal names so the example is shareable.
- Drop the 'monitoring table holds infra metrics' note (team-internal).
- Drop the verified Grafana query patterns section (team-internal sensor
lists and table names).
- Update env-var references: TIMESCALE_TABLE (with TIMESCALE_SEASON
fallback) replaces POSTGRES_TABLE; remove INFLUX_* references.
- Update slicks examples to match the merged main API:
* discover_sensors() now requires start_time and end_time
* connect_timescaledb() now has (dsn, schema, table) signature
* env-var auto-connect note uses POSTGRES_DSN + TIMESCALE_TABLE
- Same overall structure as the active (gitignored) prompt-guide.txt,
but generic.
Sandbox slicks wiring (matches the slicks v0.3.0 TimescaleDB backend on
the timescaledb-migration branch that was merged to main):
- server/installer/sandbox/Dockerfile.sandbox: install uv, then use it
for both requirements-docker.txt and the editable slicks install from
/slicks_src (a docker compose additional_context pointing at the
sibling slicks checkout). uv keeps the image layer smaller than pip
and the install deterministic. Added a build-time smoke test that
imports slicks to catch future regressions early.
- server/installer/docker-compose.yml:
* sandbox service: pass slicks source as a build-time additional_context
and bind-mount the same path at runtime so live source edits are
picked up on the next container recreate without an image rebuild
(controlled by SLICKS_HOST_PATH env var, default points at
/home/ubuntu/projects/slicks).
* sandbox service: export POSTGRES_DSN, TIMESCALE_TABLE,
TIMESCALE_SEASON, and POSTGRES_TABLE so slicks auto-connects on
import. The published PyPI slicks (0.2.3) is the InfluxDB backend
and would not be importable as TimescaleDB-aware code; install from
local source instead.
- server/installer/sandbox/requirements-docker.txt: drop the
'slicks>=0.2.0' PyPI pin. Slicks is now installed editable from the
local source above; the pin was pulling the InfluxDB-only release.
- server/installer/sandbox/requirements.txt: add psycopg2-binary
(slicks depends on it; listing explicitly so the layer order is
stable across slicks extras changes).
Code-generator Dockerfile fix:
- server/installer/sandbox/Dockerfile: the previous version only COPY'd
prompt-guide.txt.example, so the active (gitignored) prompt-guide.txt
was silently being ignored at every build. Switched to COPY
prompt-guide.txt* ./ (with the existing fallback to copy the example
when the active file is missing). This restores the team's
customization workflow where the active prompt-guide lives in the
build context and gets shipped to the container.
Model bump:
- server/installer/.env.example: ANTHROPIC_MODEL default MiniMax-M2.7
-> MiniMax-M3.
- server/installer/sandbox/code_generator.py: same bump in the
os.getenv default so a missing env var falls back to M3.
Tested end-to-end: code-generator /api/generate-code returns
result.status=success with a PNG, slicks.fetch_telemetry() returns
407k rows from a real TimescaleDB window, and the slackbot -> code-gen
-> sandbox chain still works.
Adds an npm 'overrides' block to the root package.json to force patched versions of five transitive dependencies that had Dependabot alerts: lodash -> ^4.18.0 (CVE-2026-4800, CVE-2026-2950) qs -> ^6.15.2 (CVE-2026-8723) picomatch -> ^2.3.2 (CVE-2026-32233) fast-uri -> ^3.1.2 (CVE-2026-6321, CVE-2026-6322) serialize-javascript -> ^7.0.5 (CVE-2026-34043) Regenerated package-lock.json with 'npm install --package-lock-only' (no node_modules write, no network risk beyond what 'install' needs to resolve). Verified the lock now pins: picomatch 2.3.2 (remaining 4 packages aren't pulled into this lock).
…in CVEs react-router was a direct dep pinned at ^7.9.3; multiple CVEs (DoS via path expansion, RCE via turbo-stream, XSS via redirects) require >= 7.15.0. Bumping the direct dep range is cleaner than an override (npm errors out on an override that conflicts with a direct dep). Also adds an npm 'overrides' block to force patched transitive deps: lodash -> ^4.18.0 qs -> ^6.15.2 picomatch -> ^2.3.2 fast-uri -> ^3.1.2 serialize-javascript -> ^7.0.5 Regenerated package-lock.json. Verified the lock now pins: lodash 4.18.1 qs 6.15.2 picomatch 2.3.2 fast-uri 3.1.2 serialize-javascript 7.0.5 react-router 7.16.0
react-router-dom was a direct dep pinned at ^7.9.3; same CVE class as pecan's react-router (DoS, RCE, XSS via redirects) requires >= 7.15.0. Bumped the direct dep range. Also adds an npm 'overrides' block to force patched transitive deps: lodash -> ^4.18.0 qs -> ^6.15.2 picomatch -> ^2.3.2 fast-uri -> ^3.1.2 serialize-javascript -> ^7.0.5 Regenerated package-lock.json. Verified the lock now pins: lodash 4.18.1 picomatch 2.3.2 fast-uri 3.1.2 serialize-javascript 7.0.5 react-router-dom 7.16.0
grafana-bridge is a minimal Express app (single dep) but still pulls in the same vulnerable transitive packages. Adds an 'overrides' block to force patched versions: lodash -> ^4.18.0 qs -> ^6.15.2 picomatch -> ^2.3.2 fast-uri -> ^3.1.2 serialize-javascript -> ^7.0.5 Regenerated package-lock.json. Verified qs pinned at 6.15.2.
Adds a charging dashboard that posts one self-updating Slack message (post once, chat.update every 5s, like the file-uploader) while charging the accumulator via the Kvaser bridge on the internal (pecan-dev) build. Bot (server/installer/slackbot): - charge_dashboard.py: TUI renderer (SoC bar, pack/cell/temp/alert lines, per-module sparkline + stats), per-session manager, stdlib HTTP receiver (POST /charging/state behind CF Zero Trust + shared-token auth), heartbeat staleness. - soc_model.py: data-derived SOC/ETA from the wfr26 audit. PackCurrent/BMS SOC are dead sentinels and PackVoltage is absent, so SOC is an OCV->SOC lookup on the limiting cell and ETA integrates a CV taper; phase is from the voltage trend. - Wired into slack_bot.py __main__; compose port/env; Dockerfile. - unittests: 17 (renderer, formatters, session updates, HTTP auth, model). Pecan: - chargingSnapshot.ts: pure, mockable snapshot builder mirroring the accumulator page; sentinel-guards the dead signals; derives pack voltage from the series-cell sum. - useChargingDashboard.ts: 5s poster, gated on VITE_INTERNAL + live Kvaser source (:9081). Never broadcasts the demo relay or replay data. - vitest: 15 (aggregation, alert chips, dead-signal guards, broadcast gate). Co-authored-by: Haorui Zhou <[email protected]>
Reconciles main's line + the charging-dashboard feature with dev's Constellation work, keeping the Constellation changes. Clean auto-merge (no textual conflicts); validated post-merge: tsc clean, 34 tests pass (17 slackbot + 15 charging + 2 constellation). Co-authored-by: Haorui Zhou <[email protected]>
bd79da8 to
a3a6981
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
This PR introduces an internal-only “charging dashboard” that relays accumulator charging snapshots from Pecan to a Slackbot-rendered, self-updating Slack message, while also bringing dev up to date with main (plus a broad set of deployment/health/testing improvements across UTS, server stack, and tooling).
Changes:
- Adds a Pecan → Slackbot charging snapshot pipeline (5s posting loop, snapshot builder, Slack TUI renderer/HTTP receiver, and tests), gated to internal builds and live Kvaser data only.
- Implements/extends unified health reporting for UTS (new
/healthendpoint consumer + WS client counting + Timescale status publishing) and adds LAN-sender based integration tests and compose targets. - Updates deployment docs/compose files/workflows across UTS + server installer, and adds Flight Recorder live relay (Cloudflare Worker + client relay service).
Reviewed changes
Copilot reviewed 104 out of 112 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| WEBSOCKET_PROTOCOL.md | Updates protocol documentation for role-specific uplink paths and statuses. |
| universal-telemetry-software/WEBSOCKET_RUNTIME_NOTES.md | Adds runtime/deployment-only notes to keep protocol spec canonical. |
| universal-telemetry-software/tests/test_websocket_client_count_health.py | Adds unit tests for WS client count snapshot + Redis encoding. |
| universal-telemetry-software/tests/test_status_server.py | Adds tests for health snapshot loading/staleness behavior. |
| universal-telemetry-software/tests/test_status_mosaic.py | Adds JS-level test for status page mosaic timer behavior. |
| universal-telemetry-software/tests/test_macbook_base_lan_sender.py | Adds smoke tests for the MacBook base stack using LAN sender traffic. |
| universal-telemetry-software/tests/test_lan_sender.py | Adds base-only integration tests using lan_sender.py fake car. |
| universal-telemetry-software/src/websocket_bridge.py | Publishes WS client counts to Redis for health reporting; adds periodic refresher. |
| universal-telemetry-software/src/timescale_bridge.py | Publishes periodic Timescale status into Redis with a shared payload helper. |
| universal-telemetry-software/src/status_server.py | Adds /health endpoint backed by a health snapshot file + staleness logic. |
| universal-telemetry-software/src/redis_utils.py | Adds Redis connection retries with exponential backoff. |
| universal-telemetry-software/src/lan_sender.py | Adds fake-car UDP sender for stack testing without hardware. |
| universal-telemetry-software/src/config.py | Adds REDIS_WS_CLIENTS_KEY constant. |
| universal-telemetry-software/README.md | Updates architecture/deploy/monitoring docs including /health. |
| universal-telemetry-software/main.py | Adds Timescale “auto” enable mode with DSN reachability probe. |
| universal-telemetry-software/lan_sender.sh | Adds wrapper script to run LAN sender without venv activation. |
| universal-telemetry-software/Dockerfile | Adds lan_sender.sh into the image. |
| universal-telemetry-software/deploy/WHICH_ONE.md | Updates guidance on which deploy targets to use (car systemd vs base compose). |
| universal-telemetry-software/deploy/wfr-hotspot.service | Adds systemd unit to enable a base-station Wi-Fi hotspot. |
| universal-telemetry-software/deploy/offline/README.md | Updates offline bundle instructions/images for new repo naming and profiles. |
| universal-telemetry-software/deploy/MACBOOK_DEPLOY.md | Refactors base station setup docs; adds installer + /health guidance. |
| universal-telemetry-software/deploy/docker-compose.staging.yml | Updates GHCR image paths for staging. |
| universal-telemetry-software/deploy/docker-compose.rpi-base.yml | Updates GHCR image paths for RPi base. |
| universal-telemetry-software/deploy/docker-compose.macbook-base.yml | Refactors MacBook base stack into profiles; updates GHCR image paths. |
| universal-telemetry-software/deploy/docker-compose.lan-sender-test.yml | Adds compose stack for LAN sender integration testing. |
| universal-telemetry-software/deploy/ci-validate-compose.py | Adds CI validation to ensure compose structure + no local build: directives. |
| universal-telemetry-software/deploy/CAR_DEPLOY.md | Updates car deployment docs for new repo pathing. |
| universal-telemetry-software/deploy/.env.macbook | Commits safe MacBook LAN defaults and profile guidance. |
| universal-telemetry-software/conftest.py | Adjusts pytest pathing for cone-detection dependency. |
| universal-telemetry-software/checklist/SETUP_CARD.tex | Updates setup card paths to new repo naming. |
| server/installer/VPS_RECOVERY.md | Updates recovery paths and clarifies lap-detector status. |
| server/installer/slackbot/test_soc_model.py | Adds unit tests for derived SOC/ETA model. |
| server/installer/slackbot/test_charge_dashboard.py | Adds unit tests for Slack charging dashboard renderer/session/HTTP auth. |
| server/installer/slackbot/slack_bot.py | Wires in charge dashboard HTTP receiver + new stats reporting + daily scheduler. |
| server/installer/slackbot/requirements.txt | Adds psycopg2-binary for DB-backed stats reporting. |
| server/installer/slackbot/Dockerfile | Copies charge dashboard + SOC model sources into the image. |
| server/installer/sandbox/requirements.txt | Adds psycopg2-binary to sandbox deps. |
| server/installer/sandbox/requirements-docker.txt | Adjusts sandbox deps/comments; makes DB deps explicit. |
| server/installer/sandbox/rag_viz.py | Adds RAG vector visualization script. |
| server/installer/sandbox/Dockerfile.sandbox | Switches to uv, supports editable slicks install via additional context. |
| server/installer/sandbox/Dockerfile | Ensures prompt-guide customization is copied correctly; adds anomaly_scan.py. |
| server/installer/sandbox/code_generator.py | Updates default model to MiniMax-M3. |
| server/installer/README.md | Minor wording updates (lap-detector status). |
| server/installer/LOCAL_STACK.md | Updates backup script path references. |
| server/installer/grafana-bridge/package.json | Adds dependency overrides for transitive security bumps. |
| server/installer/grafana-bridge/package-lock.json | Updates locked transitive versions (e.g., qs). |
| server/installer/docker-compose.yml | Adds charge dashboard port/token env + sandbox slicks additional context + model update. |
| server/installer/data-downloader/frontend/src/components/data-download.tsx | Adds theme-aware Plotly styling. |
| server/installer/data-downloader/frontend/src/App.tsx | Adds theme toggle + persists theme selection; wires theme into chart. |
| server/installer/data-downloader/frontend/index.html | Applies theme pre-paint to avoid flash. |
| server/installer/data-downloader/backend/services.py | Adds Timescale lag freshness check for newest season table. |
| server/installer/data-downloader/backend/app.py | Adds /api/timescale-health endpoint; extends base healthcheck fields. |
| server/installer/backup-dashboards.py | Updates backup script path references. |
| server/installer/.env.example | Updates default AI model name. |
| server/docs/containers/lap-detector.md | Marks lap-detector as intentionally disabled/tabled. |
| pecan/test-gh-pages.js | Updates GH Pages base path to new repo name. |
| pecan/src/vite-env.d.ts | Adds env vars for charge relay URL/token. |
| pecan/src/services/WebSocketService.ts | Prefers localhost WS on localhost pages; improves failover ordering. |
| pecan/src/services/WebSocketService.test.ts | Adds test for localhost-first WS candidate ordering. |
| pecan/src/services/TelemetryHandler.ts | Treats relay heartbeat CAN ID as diagnostic heartbeat (not telemetry). |
| pecan/src/services/TelemetryHandler.test.ts | Adds tests for heartbeat routing for both UTS + relay heartbeat IDs. |
| pecan/src/pages/Trace.tsx | Removes redundant memoization in trace enrichment path. |
| pecan/src/pages/Accumulator.tsx | Starts charging dashboard relay hook on accumulator page. |
| pecan/src/lib/useDataStore.ts | Adds version-based gating for map rebuild + batches cold-state updates. |
| pecan/src/lib/useChargingDashboard.ts | Adds internal-only charging snapshot poster (Kvaser/live-gated). |
| pecan/src/lib/useChargingDashboard.test.ts | Adds tests for demo-host exclusion + Kvaser-only gating. |
| pecan/src/lib/DataStore.ts | Adds version counter + reduces allocations when rounding is a no-op. |
| pecan/src/lib/DataStore.test.ts | Adds tests for version counter and rounding/immutability expectations. |
| pecan/src/lib/chargingSnapshot.ts | Adds snapshot builder with sentinel guards + derived pack voltage. |
| pecan/src/lib/chargingSnapshot.test.ts | Adds tests for aggregation, alerts, sentinel guards. |
| pecan/src/components/SettingsModal.tsx | Adds setting for plot axis range source selection (DBC vs dynamic). |
| pecan/src/components/PlotManager.tsx | Uses the stored plot axis range source to enable/disable DBC ranges. |
| pecan/src/components/comms/RadioStatChips.tsx | Switches heartbeat tracking to diagnostic heartbeat ID constant. |
| pecan/README.md | Updates connection defaults (localhost WS on localhost). |
| pecan/package.json | Bumps react-router; adds dependency overrides. |
| pecan/package-lock.json | Updates lockfile for dependency bumps/overrides. |
| package.json | Adds dependency overrides at repo root. |
| package-lock.json | Updates root package name + dependency lock changes. |
| livestream/src/widgets.jsx | Removes motor-temp warning chip. |
| livestream/src/telemetry-store.jsx | Updates signal aliases and adds additional telemetry signals. |
| livestream/src/overlays.jsx | Updates overlay stats (dc bus/temps) and improves speed calculation. |
| flight-recorder/src/services/WebSocketService.ts | Emits raw “wire” frames to listeners (for relay forwarding). |
| flight-recorder/src/services/LiveRelayService.ts | Adds best-effort live relay forwarding + synthetic heartbeat injection. |
| flight-recorder/src/services/LiveRelayService.test.ts | Adds tests for session creation, heartbeat, and demo-source blocking. |
| flight-recorder/relay-worker/wrangler.toml | Adds Wrangler config for relay worker + DO binding/migration. |
| flight-recorder/relay-worker/tsconfig.json | Adds strict TS config for the worker. |
| flight-recorder/relay-worker/src/index.ts | Implements Worker + Durable Object WS relay with session creation endpoint. |
| flight-recorder/relay-worker/README.md | Documents relay worker usage, URLs, and security model. |
| flight-recorder/relay-worker/package.json | Adds Wrangler/TS dev setup for the worker. |
| flight-recorder/README.md | Documents Flight Recorder workflow and optional live relay architecture. |
| flight-recorder/package.json | Adds test script; bumps react-router-dom; adds overrides. |
| AGENTS.md | Adds repo-wide agent orientation + canonical docs pointers. |
| .hermes/plans/2026-05-21_124500-unified-health-endpoint.md | Adds design plan for unified health endpoint rollout. |
| .gitignore | Updates workspace ignore name and server section header. |
| .github/workflows/telemetry-ci.yml | Adds macbook compose validation + LAN sender test jobs and gating. |
| .github/workflows/slackbot-ci.yml | Adds Slackbot CI (ruff + import smoke test + docker build). |
| .github/workflows/package-cleanup.yml | Expands GHCR package cleanup targets. |
| .github/workflows/offline-bundle.yml | Updates offline bundle image names to new GHCR paths. |
| .github/workflows/macbook-installer-ci.yml | Adds installer CI (shellcheck, macOS logic smoke test, Linux e2e). |
Files not reviewed (4)
- flight-recorder/package-lock.json: Language not supported
- flight-recorder/relay-worker/package-lock.json: Language not supported
- pecan/package-lock.json: Language not supported
- server/installer/grafana-bridge/package-lock.json: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+65
to
+82
| async def _publish_client_count() -> None: | ||
| """Publish the current WebSocket client count for the HTTP health snapshot.""" | ||
| try: | ||
| r = redis.from_url(REDIS_URL) | ||
| await r.set(REDIS_WS_CLIENTS_KEY, json.dumps(_client_count_snapshot()), ex=10) | ||
| await r.aclose() | ||
| except Exception as e: | ||
| logger.debug("Could not publish WebSocket client count: %s", e) | ||
|
|
||
|
|
||
| async def _client_count_publisher() -> None: | ||
| """Keep the health snapshot client count fresh even when no clients churn.""" | ||
| while not shutdown_event.is_set(): | ||
| await _publish_client_count() | ||
| try: | ||
| await asyncio.wait_for(shutdown_event.wait(), timeout=2.0) | ||
| except asyncio.TimeoutError: | ||
| pass |
Comment on lines
+18
to
+25
| """Return a connected synchronous Redis client, retrying on failure. | ||
|
|
||
| Args: | ||
| url: Redis connection URL. | ||
| retries: Maximum connection attempts before giving up (default 10). | ||
| backoff: Seconds to wait between retries, multiplied by 1.5 each attempt. | ||
| With default values: ~25s total (1+1.5+2.25+3.375+...). | ||
| """ |
Comment on lines
+16
to
+24
| environment: | ||
| - ROLE=base | ||
| - REMOTE_IP=127.0.0.1 # Will be overridden by lan_sender — base listens on all interfaces | ||
| - UDP_PORT=5005 | ||
| - TCP_PORT=5006 | ||
| - REDIS_URL=redis://base-redis:6379/0 | ||
| - TIMESCALE_TABLE=wfr26 | ||
| - ENABLE_TIMESCALE_LOGGING=true | ||
| depends_on: |
Comment on lines
+849
to
+856
| from charge_dashboard import ChargeDashboard, start_http_server | ||
| _charge_dash = ChargeDashboard(web_client, DEFAULT_CHANNEL) | ||
| start_http_server( | ||
| _charge_dash, | ||
| port=int(os.environ.get("CHARGE_DASHBOARD_PORT", "9099")), | ||
| token=os.environ.get("CHARGE_RELAY_TOKEN") or None, | ||
| ) | ||
| except Exception as e: |
- slackbot-ci: stub SocketModeRequest/Response on the slack_sdk stub modules (import was failing with 'cannot import name SocketModeRequest') and provision /app/logs so the module-level mkdir doesn't fail on the runner. - slack_bot: require CHARGE_RELAY_TOKEN before starting the charge-dashboard receiver; the port is host-published and must not accept unauthenticated POSTs. - docker-compose.lan-sender-test: set POSTGRES_DSN to the timescaledb service (default DSN points at localhost, which is the base container here). - websocket_bridge: aclose the Redis client via try/finally so a failing set doesn't leak the connection. - redis_utils: correct get_sync_client docstring (default retries=5, ~8s).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
Two things bundled because the feature is built on
mainanddevwas ~20 commits behind:devwhile keeping all the Constellation work (mergedorigin/dev, clean auto-merge, no textual conflicts).Charging dashboard
A single self-updating Slack message (post once,
chat.updateevery 5s, like the file-uploader) showing accumulator charge state while charging via the Kvaser bridge on the internal pecan-dev (VITE_INTERNAL) build, behind Cloudflare Zero Trust.Bot (
server/installer/slackbot/)charge_dashboard.py— TUI renderer (SoC bar, pack/cell/temp/alert lines, per-module sparkline + stats), per-session manager, stdlib HTTP receiver (POST /charging/state, shared-token auth), heartbeat staleness.soc_model.py— data-derived SOC/ETA from the wfr26 audit. Key finding:PackCurrentand BMSSOCare dead sentinels andPackVoltageis absent, so SOC is an OCV→SOC lookup on the limiting cell, ETA integrates a CV taper, and phase is inferred from the voltage trend.slack_bot.py__main__; compose port/env (CHARGE_DASHBOARD_PORT,CHARGE_RELAY_TOKEN); Dockerfile.Pecan
lib/chargingSnapshot.ts— pure, mockable snapshot builder mirroring the accumulator page; sentinel-guards the dead signals; derives pack voltage from the series-cell sum.lib/useChargingDashboard.ts— 5s poster, gated onVITE_INTERNAL+ live Kvaser source (:9081). Never broadcasts the demo relay or replay data.Safety: no demo data to Slack
isKvaserSource()returns true only for the Kvaser bridge:9081, hard-excludesws-demo.westernformularacing.org, andtick()bails on non-live (replay) data. Covered by tests.Tests
tscclean; charging + Constellation suites pass.Follow-ups
PackStatusCAN decode (the-3276/0sentinels) would unlock real coulomb counting + direct SOC.