Skip to content

Conversation

@dastine0308
Copy link
Contributor

@dastine0308 dastine0308 commented Nov 20, 2025

PR Description

  • Introduced GrafanaWebSocket class to handle WebSocket connections for Grafana.
  • Updated BackendController to instantiate GrafanaWebSocket and broadcast telemetry data.
  • Modified LapController to send lap data to Grafana WebSocket clients.
  • Added @types/ws dependency for TypeScript support in WebSocket handling.
  • Updated package.json to include new dependencies and types.

Screenshot

image image-20251118-002623

Summary by CodeRabbit

  • New Features

    • Real-time WebSocket broadcasting for telemetry and lap data to support Grafana visualizer.
  • Documentation

    • New Grafana integration guide with setup and WebSocket instructions.
    • README updated with link to Grafana visualizer docs.
  • Chores

    • Added example environment file for server configuration.
    • Updated dependencies: removed SQLite packages; added WebSocket support and related types.

✏️ Tip: You can customize this high-level summary in your review settings.

@dastine0308 dastine0308 requested a review from a team as a code owner November 20, 2025 02:01
@vercel
Copy link

vercel bot commented Nov 20, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
helios-telemetry Ready Ready Preview Comment Nov 29, 2025 6:26pm

@coderabbitai
Copy link

coderabbitai bot commented Nov 20, 2025

Walkthrough

Adds a native WebSocket broadcaster and types, wires it into BackendController and LapController to emit telemetry and lap data to Grafana, updates server dependencies (remove sqlite*, add ws/@types/ws and rimraf), adds Grafana docs and a server .env example with GRAFANA_WS_PATH.

Changes

Cohort / File(s) Summary
Dependency updates
packages/server/package.json
Removed @types/sqlite3, sqlite, sqlite3; added ws and rimraf to dependencies; added @types/ws as a devDependency; removed rimraf from devDependencies.
WebSocket datasource & types
packages/server/src/datasources/WebSocket/WebSocket.ts, packages/server/src/datasources/WebSocket/WebSocket.types.ts
New NativeWebSocket class and WebSocketType interface: creates a WebSocketServer on a path tied to the HTTPS server, manages connections, logs lifecycle/events, and exposes broadcastPacket() and broadcastLapData() with per-client error handling; exported as default.
BackendController integration
packages/server/src/controllers/BackendController/BackendController.ts, packages/server/src/controllers/BackendController/BackendController.types.ts
Added public webSocket: NativeWebSocket to controller types; BackendController constructor initializes webSocket using GRAFANA_WS_PATH and the HTTPS server; handlePacketReceive contains a commented placeholder for native WebSocket broadcasting.
LapController changes
packages/server/src/controllers/LapController/LapController.ts
After socketIO broadcast, now calls backendController.webSocket.broadcastLapData(lapData); removed the packet.B3.LapDigital requirement from lap-completion detection (now uses lastLapPackets.length > 5).
Docs & env example
README.md, docs/GRAFANA.md, packages/server/.env.example
README: added "Grafana + Visualizer Setup" link. Added docs/GRAFANA.md with setup/run instructions for Grafana/visualizer/test publisher. Added packages/server/.env.example including GRAFANA_WS_PATH and related server env placeholders.

Sequence Diagram(s)

sequenceDiagram
    participant Source as Telemetry Source
    participant Backend as BackendController
    participant LapCtrl as LapController
    participant WS as NativeWebSocket
    participant Grafana as Grafana Client

    Source->>Backend: send packet
    Backend->>Backend: handlePacketReceive
    Backend->>WS: (optional) broadcastPacket(packet)

    Source->>LapCtrl: stream packets
    LapCtrl->>LapCtrl: detect lap (lastLapPackets.length > 5)
    LapCtrl->>WS: broadcastLapData(lapData)
    WS-->>Grafana: {type:"lapData", data:ILapData}
    LapCtrl->>Backend: persist lap
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Review focus:
    • packages/server/src/datasources/WebSocket/WebSocket.ts — lifecycle, per-client send/error handling, and types.
    • BackendController constructor ordering and environment use (GRAFANA_WS_PATH) when creating the WebSocket.
    • LapController lap-detection change (removal of LapDigital) and broadcast placement.
    • packages/server/package.json dependency edits and devDependency moves.

Poem

🐰 I hopped from rows to realtime streams,
WebSockets hum where sqlite slept,
Packets and laps now dance in beams,
Grafana watches as data leapt.
🥕 A rabbit applauds the new connect!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main objective of the pull request: establishing a WebSocket connection between Grafana and the backend, which is reflected throughout all code changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch TEL-313-Setup-Websocket-connection-between-Grafana-and-Backend-Helios-Telemetry

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 55b763f and ab2c8b9.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (1)
  • packages/server/package.json (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/server/package.json

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/server/src/controllers/BackendController/BackendController.types.ts (1)

4-19: Consider depending on the GrafanaWebSocket interface type instead of the concrete class

Right now BackendControllerTypes.grafanaWebSocket is typed as the concrete GrafanaWebSocket class. If you want easier testing or alternative implementations later, you might instead type this as GrafanaWebSocketType (from GrafanaWebSocket.types.ts) and keep the class detail local to BackendController.

packages/server/src/datasources/GrafanaWebSocket/GrafanaWebSocket.types.ts (1)

1-9: Interface matches implementation; consider whether wss should remain part of the public surface

GrafanaWebSocketType cleanly describes the two broadcast methods and underlying WebSocketServer. If consumers don’t need direct access to wss, you could drop that property (or mark it readonly) to decouple callers from ws internals and keep the abstraction narrower.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 36a9f11 and 26aaa0c.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (6)
  • packages/server/package.json (2 hunks)
  • packages/server/src/controllers/BackendController/BackendController.ts (3 hunks)
  • packages/server/src/controllers/BackendController/BackendController.types.ts (2 hunks)
  • packages/server/src/controllers/LapController/LapController.ts (1 hunks)
  • packages/server/src/datasources/GrafanaWebSocket/GrafanaWebSocket.ts (1 hunks)
  • packages/server/src/datasources/GrafanaWebSocket/GrafanaWebSocket.types.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
packages/server/src/datasources/GrafanaWebSocket/GrafanaWebSocket.types.ts (1)
packages/shared/src/types.ts (2)
  • ILapData (421-437)
  • ITelemetryData (399-399)
packages/server/src/controllers/BackendController/BackendController.ts (2)
packages/server/src/datasources/GrafanaWebSocket/GrafanaWebSocket.ts (1)
  • GrafanaWebSocket (14-81)
packages/server/src/datasources/SocketIO/SocketIO.ts (1)
  • SocketIO (20-86)
packages/server/src/datasources/GrafanaWebSocket/GrafanaWebSocket.ts (4)
packages/server/src/utils/logger.ts (1)
  • createLightweightApplicationLogger (98-102)
packages/server/src/datasources/GrafanaWebSocket/GrafanaWebSocket.types.ts (1)
  • GrafanaWebSocketType (5-9)
packages/server/src/controllers/BackendController/BackendController.ts (1)
  • BackendController (16-73)
packages/shared/src/types.ts (2)
  • ITelemetryData (399-399)
  • ILapData (421-437)
packages/server/src/controllers/BackendController/BackendController.types.ts (1)
packages/server/src/datasources/GrafanaWebSocket/GrafanaWebSocket.ts (1)
  • GrafanaWebSocket (14-81)
🔇 Additional comments (7)
packages/server/src/controllers/LapController/LapController.ts (2)

96-98: Verify BackendController initializes grafanaWebSocket property.

The constructor injects backendController and stores it as an instance variable. Ensure that BackendController properly initializes the grafanaWebSocket property during construction or before this method is invoked. If grafanaWebSocket is lazy-initialized or conditionally created, this could lead to runtime errors.


159-163: Remove unnecessary await and reconsider the review comment's premise.

The original review comment's core concern is invalid. The broadcastLapData method returns void, not a Promise, so it cannot reject or throw an unhandled error. The await at line 161 is semantically unnecessary but harmless.

Additionally, there is no null safety concern—grafanaWebSocket is properly initialized in BackendController's constructor at line 28.

Minor issue: Line 161 uses await on a void-returning function. This should be removed:

  public async handleLapData(lapData: ILapData) {
    await this.backendController.socketIO.broadcastLapData(lapData);
-   await this.backendController.grafanaWebSocket.broadcastLapData(lapData);
+   this.backendController.grafanaWebSocket.broadcastLapData(lapData);
    await this.backendController.dynamoDB.insertLapData(lapData);
  }

Likely an incorrect or invalid review comment.

packages/server/package.json (1)

19-35: ws / @types/ws dependency additions look appropriate; confirm runtime environment compatibility

The new ws and @types/ws entries match how GrafanaWebSocket is imported and typed elsewhere. Just ensure the deployed Node.js version is compatible with ws@^8.18.3 and that your lockfile is updated so CI/prod run the same versions.

Please double‑check the ws compatibility matrix / release notes for your Node version and update the lockfile (package-lock.json / pnpm-lock.yaml / yarn.lock) accordingly if not already done.

packages/server/src/controllers/BackendController/BackendController.ts (2)

7-31: GrafanaWebSocket wiring into BackendController looks sound

Importing GrafanaWebSocket, storing it as a public field, and instantiating it alongside SocketIO and SolarMQTTClient keeps all datasources consistently managed by BackendController. Passing httpsServer and this mirrors the existing Socket.IO pattern and avoids runtime cycles because the reverse dependency is type‑only.


54-58: Extending packet broadcast to Grafana WebSocket aligns with existing flow

Forwarding every telemetry packet to grafanaWebSocket.broadcastPacket right after the Socket.IO broadcast keeps Grafana clients in lockstep with the frontend without altering database or lap‑handling logic. The change is minimal and keeps handlePacketReceive responsibilities clear.

packages/server/src/datasources/GrafanaWebSocket/GrafanaWebSocket.ts (2)

1-39: Connection setup and logging for Grafana WebSocket are consistent with existing patterns

Binding WebSocketServer to the shared HTTP(S) server on /grafana-ws, logging the client’s remote address, registering per‑socket listeners, and sending an initial JSON “connection” message all mirror typical ws usage and fit well with the existing datasource style (e.g., SocketIO).


72-80: Per-socket close/error listeners are wired correctly

The initializeWebSocketListeners helper cleanly centralizes close and error handling with appropriate logging, matching the style used elsewhere in the codebase.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 26aaa0c and 3f35cd6.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (4)
  • README.md (1 hunks)
  • docs/GRAFANA.md (1 hunks)
  • packages/server/package.json (2 hunks)
  • packages/server/src/datasources/GrafanaWebSocket/GrafanaWebSocket.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • README.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/server/src/datasources/GrafanaWebSocket/GrafanaWebSocket.ts
🧰 Additional context used
🪛 markdownlint-cli2 (0.18.1)
docs/GRAFANA.md

69-69: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


86-86: Hard tabs
Column: 1

(MD010, no-hard-tabs)


87-87: Hard tabs
Column: 1

(MD010, no-hard-tabs)


88-88: Hard tabs
Column: 1

(MD010, no-hard-tabs)

🔇 Additional comments (3)
docs/GRAFANA.md (1)

75-89: WebSocket setup section is clear and well-documented.

The quick setup instructions correctly reference the GRAFANA_WS_PATH environment variable and guide users through the Grafana UI steps. The default path /grafana-ws aligns with typical WebSocket datasource conventions. Instructions are actionable and match the integration approach described in the PR objectives.

packages/server/package.json (2)

31-31: Dependency placement is correct.

"ws" correctly belongs in dependencies (runtime requirement) and "@types/ws" correctly in devDependencies (TypeScript types only). This aligns with the GrafanaWebSocket integration described in the PR objectives.

Also applies to: 40-40


31-31: I have confirmation that [email protected] has no known critical security vulnerabilities. Now I need to verify @types/[email protected] and confirm Node 18+ compatibility for both packages.

I've confirmed both packages' security status. Now let me verify the actual dependencies in the package.json file:

Based on my verification, both packages have clean security records and are compatible with Node 18+. The original review comment's concerns have been addressed.


Security verification complete: [email protected] and @types/[email protected] are both secure for Node 18+.

@types/ws has a consistent security record with zero vulnerabilities across all versions including 8.18.1, and [email protected] has no direct vulnerabilities in Snyk's database. The high-severity CVE-2024-37890 (DoS via excessive headers) was fixed in [email protected] and is not present in 8.18.3. Both versions are compatible with Node 18+.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/server/src/controllers/LapController/LapController.ts (1)

161-161: Remove unnecessary await on synchronous method.

The broadcastLapData method is synchronous (returns void, not Promise<void>), so using await here is misleading. While not breaking, it suggests the method is async when it's not.

Apply this diff to remove the unnecessary await:

   await this.backendController.socketIO.broadcastLapData(lapData);
-  await this.backendController.grafanaWebSocket.broadcastLapData(lapData);
+  this.backendController.grafanaWebSocket.broadcastLapData(lapData);
   await this.backendController.dynamoDB.insertLapData(lapData);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3f35cd6 and 5fe01b1.

📒 Files selected for processing (2)
  • packages/server/src/controllers/BackendController/BackendController.ts (2 hunks)
  • packages/server/src/controllers/LapController/LapController.ts (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/server/src/controllers/BackendController/BackendController.ts (1)
packages/server/src/datasources/GrafanaWebSocket/GrafanaWebSocket.ts (1)
  • GrafanaWebSocket (16-83)
🔇 Additional comments (1)
packages/server/src/controllers/BackendController/BackendController.ts (1)

7-7: LGTM! GrafanaWebSocket integration looks good.

The import, field declaration, and instantiation are correctly implemented. The initialization order is appropriate since grafanaWebSocket is instantiated before lapController (which uses it).

Note: There's a past review suggestion about creating an abstract WebSocket class for extensibility if other services will use WebSocket. Consider this for future refactoring.

Also applies to: 21-21, 28-28

Comment on lines 194 to 198
if (
// packet.B3.LapDigital && //TEST: commented out for broadcasting lap data to grafana
this.lastLapPackets.length > 5
) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Restore the lap completion condition - current logic is broken.

The commented-out packet.B3.LapDigital check was the primary lap completion trigger. Without it, lap data will be broadcast on every packet after the first 5 packets accumulate, not just when a lap is actually completed. This will:

  • Generate spurious lap records in the database
  • Spam the Grafana dashboard with incorrect lap data
  • Make the lap tracking system unusable

The comment indicates this is a test change, but it breaks production functionality and should not be merged.

Apply this diff to restore the proper lap detection logic:

-  if (
-    // packet.B3.LapDigital && //TEST: commented out for broadcasting lap data to grafana
-    this.lastLapPackets.length > 5
-  ) {
+  if (packet.B3.LapDigital && this.lastLapPackets.length > 5) {

If you need to test Grafana lap broadcasting without actual lap completion events, consider adding a separate test endpoint or feature flag rather than breaking the production lap detection logic.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (
// packet.B3.LapDigital && //TEST: commented out for broadcasting lap data to grafana
this.lastLapPackets.length > 5
) {
if (packet.B3.LapDigital && this.lastLapPackets.length > 5) {
🤖 Prompt for AI Agents
In packages/server/src/controllers/LapController/LapController.ts around lines
194-197, the lap completion guard was weakened by commenting out the
packet.B3.LapDigital check, causing lap data to be broadcast after five packets
instead of only on actual lap completions; restore the original condition by
re-enabling the packet.B3.LapDigital (or equivalent lap-completed flag) in the
if statement so broadcasting only occurs when a lap completion event is present,
and if needed add a separate test flag or endpoint to allow forced/test
broadcasts without modifying production lap-detection logic.

@dastine0308 dastine0308 force-pushed the TEL-313-Setup-Websocket-connection-between-Grafana-and-Backend-Helios-Telemetry branch from 5fe01b1 to ecdd67f Compare November 22, 2025 21:45
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
packages/server/src/controllers/BackendController/BackendController.ts (1)

7-29: GrafanaWebSocket wiring is consistent with existing SocketIO pattern

The new GrafanaWebSocket import, field, and constructor wiring mirror how SocketIO is integrated (server + BackendController passed in), which keeps the data-source patterns consistent. I don’t see correctness issues with this setup.

As a non-blocking design thought (echoing the earlier review), if more WS-based integrations appear later, an abstract WS layer or common interface/factory could help decouple BackendController from concrete implementations, but this can be deferred until there’s a second consumer.

🧹 Nitpick comments (1)
packages/server/src/controllers/BackendController/BackendController.ts (1)

56-58: Commented-out Grafana packet broadcast is fine for current scope

Leaving grafanaWebSocket.broadcastPacket(message) commented out with the note that only ILapData should go over WS matches the current product decision and avoids double-sending telemetry that already reaches Grafana via MQTT.

Once/if you decide to stream full telemetry via WS, consider either:

  • Removing the comment and enabling this line, or
  • Guarding WS telemetry behind a config flag instead of a hard-coded comment.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5fe01b1 and ecdd67f.

📒 Files selected for processing (2)
  • packages/server/src/controllers/BackendController/BackendController.ts (3 hunks)
  • packages/server/src/controllers/LapController/LapController.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/server/src/controllers/LapController/LapController.ts
🧰 Additional context used
🧬 Code graph analysis (1)
packages/server/src/controllers/BackendController/BackendController.ts (2)
packages/server/src/datasources/GrafanaWebSocket/GrafanaWebSocket.ts (1)
  • GrafanaWebSocket (16-83)
packages/server/src/datasources/SocketIO/SocketIO.ts (1)
  • SocketIO (20-86)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
packages/server/src/datasources/WebSocket/WebSocket.ts (1)

28-39: Optionally guard the initial ws.send in the connection handler with try/catch for consistency

You already wrap ws.send in broadcastPacket/broadcastLapData with try/catch, which is great. The initial connection handshake ws.send in the "connection" handler is still unguarded. While failures here are unlikely, wrapping it similarly would make the behaviour consistent and ensure a sudden state change on the socket can’t throw synchronously:

-    this.wss.on("connection", (ws: WebSocket, req: IncomingMessage) => {
+    this.wss.on("connection", (ws: WebSocket, req: IncomingMessage) => {
       logger.info(
         `WebSocket client connected from ${req.socket.remoteAddress}`,
       );
       this.initializeWebSocketListeners(ws);
-      ws.send(
-        JSON.stringify({
-          message: "Connected to Helios Telemetry WebSocket",
-          type: "connection",
-        }),
-      );
+      try {
+        ws.send(
+          JSON.stringify({
+            message: "Connected to Helios Telemetry WebSocket",
+            type: "connection",
+          }),
+        );
+      } catch (error) {
+        logger.error("Error sending initial WebSocket connection message", error);
+      }
     });

Not mandatory, but it aligns this path with your broadcast error handling.

🧹 Nitpick comments (1)
packages/server/src/datasources/WebSocket/WebSocket.types.ts (1)

1-9: Interface surface is clear; consider whether wss needs to be exposed

WebSocketType cleanly describes the broadcasting API. If no callers outside the datasource need direct access to the underlying WebSocketServer, you might consider omitting wss from the interface and keeping it as an implementation detail for easier future swapping/mocking. If it is needed, this is fine as-is.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ecdd67f and 83cd549.

📒 Files selected for processing (5)
  • packages/server/src/controllers/BackendController/BackendController.ts (2 hunks)
  • packages/server/src/controllers/BackendController/BackendController.types.ts (2 hunks)
  • packages/server/src/controllers/LapController/LapController.ts (2 hunks)
  • packages/server/src/datasources/WebSocket/WebSocket.ts (1 hunks)
  • packages/server/src/datasources/WebSocket/WebSocket.types.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/server/src/controllers/BackendController/BackendController.types.ts
  • packages/server/src/controllers/LapController/LapController.ts
🧰 Additional context used
🧬 Code graph analysis (3)
packages/server/src/datasources/WebSocket/WebSocket.types.ts (1)
packages/shared/src/types.ts (2)
  • ILapData (421-437)
  • ITelemetryData (399-399)
packages/server/src/controllers/BackendController/BackendController.ts (3)
packages/server/src/controllers/BackendController/BackendController.types.ts (1)
  • BackendControllerTypes (10-19)
packages/server/src/controllers/LapController/LapController.ts (1)
  • LapController (40-506)
packages/server/src/datasources/WebSocket/WebSocket.ts (1)
  • NativeWebSocket (14-88)
packages/server/src/datasources/WebSocket/WebSocket.ts (4)
packages/server/src/utils/logger.ts (1)
  • createLightweightApplicationLogger (98-102)
packages/server/src/datasources/WebSocket/WebSocket.types.ts (1)
  • WebSocketType (5-9)
packages/server/src/controllers/BackendController/BackendController.ts (1)
  • BackendController (18-75)
packages/shared/src/types.ts (2)
  • ITelemetryData (399-399)
  • ILapData (421-437)
🔇 Additional comments (1)
packages/server/src/controllers/BackendController/BackendController.ts (1)

10-31: Grafana WebSocket wiring and conditional usage look consistent

The NativeWebSocket integration (env-based grafanaWsPath, ctor wiring, and webSocket field) is straightforward and matches how SocketIO is attached to the HTTPS server. Leaving broadcastPacket commented out with the inline note keeps the intent clear that only lap data is sent over WS for now. No issues from this change set.

Also applies to: 58-60

@dastine0308 dastine0308 force-pushed the TEL-313-Setup-Websocket-connection-between-Grafana-and-Backend-Helios-Telemetry branch from 83cd549 to 456d971 Compare November 22, 2025 23:07
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (5)
docs/GRAFANA.md (2)

69-71: Specify a language for the fenced code block

The fenced block showing http://localhost:3000 has no language specifier. To satisfy markdownlint and improve rendering, change the opening fence to something like:

-```
+```text
 http://localhost:3000

---

`85-88`: **Replace hard tabs with spaces in the nested list**

Lines 86–88 are indented with hard tab characters. Replace each leading tab with spaces (2 or 4, consistent with the repo style) so markdownlint passes and rendering is consistent:

```diff
-	- Click **Explore** in the left-hand menu.
-	- For **Data source**, choose **WebSocket API** (the WebSocket data source in the Telemetry-Visualizer setup).
-	- In the **Field** input enter: `$.data`
+  - Click **Explore** in the left-hand menu.
+  - For **Data source**, choose **WebSocket API** (the WebSocket data source in the Telemetry-Visualizer setup).
+  - In the **Field** input enter: `$.data`
packages/server/src/controllers/LapController/LapController.ts (1)

194-236: Restore LapDigital guard so laps are only finalized on real lap completions

With packet.B3.LapDigital commented out, the condition now fires whenever lastLapPackets.length > 5, so every run of 6+ packets will be treated as a completed lap. That will spam lap records and Grafana with bogus laps and makes lap tracking unreliable.

Restore the original LapDigital check:

-    if (
-      //TEST: commented out this condition for broadcasting lap data to grafana
-      // packet.B3.LapDigital &&
-      this.lastLapPackets.length > 5
-    ) {
+    if (packet.B3.LapDigital && this.lastLapPackets.length > 5) {

If you need a way to test Grafana broadcasting without actual lap events, consider a dedicated test hook/flag instead of weakening production lap detection.

packages/server/src/datasources/WebSocket/WebSocket.ts (2)

28-39: Guard the initial ws.send in the connection handler with try/catch

You’ve added robust try/catch handling around broadcast sends, but the welcome message in the "connection" handler still calls ws.send(...) without protection. If a client disconnects immediately after connecting (or the socket isn’t in OPEN state), send can throw synchronously and bubble out of the listener.

Mirror the broadcast pattern here:

-      this.initializeWebSocketListeners(ws);
-      ws.send(
-        JSON.stringify({
-          message: "Connected to Helios Telemetry WebSocket",
-          type: "connection",
-        }),
-      );
+      this.initializeWebSocketListeners(ws);
+      try {
+        ws.send(
+          JSON.stringify({
+            message: "Connected to Helios Telemetry WebSocket",
+            type: "connection",
+          }),
+        );
+      } catch (error) {
+        logger.error("Error sending initial WebSocket handshake message", error);
+      }

This keeps a single misbehaving Grafana/WebSocket client from impacting the server.


1-3: Fix invalid export default WebSocket (type‑only import used as a value and wrong default)

WebSocket is imported with type only, but at the bottom of the file you do export default WebSocket;. That will fail type‑checking (type‑only import used as a value) and also exports the ws library’s class instead of your NativeWebSocket wrapper.

Either remove the default export or point it at NativeWebSocket:

-export default WebSocket;
+// Either remove the default export entirely:
+// (recommended if everything uses the named export)
+// export default removed
+
+// or, if you really want a default:
+export default NativeWebSocket;

Then ensure consumers import this module consistently (either named or default, but not both).

Also applies to: 88-90

🧹 Nitpick comments (3)
packages/server/.env.example (1)

5-56: Add GRAFANA_WS_PATH to the server env example for discoverability

Since GRAFANA_WS_PATH is now used by the backend and referenced in docs/GRAFANA.md, consider adding a placeholder here (e.g. GRAFANA_WS_PATH="/grafana-ws" under a WebSocket/Grafana section) so new developers see all required server env vars in one place. The existing dotenv-linter warnings about quotes/key ordering are cosmetic and can be ignored or cleaned up later if you want a zero‑warning run.

packages/server/src/controllers/LapController/LapController.ts (1)

158-163: WebSocket lap broadcast wiring looks correct; consider error‑isolation from DB writes

Adding backendController.webSocket.broadcastLapData(lapData) alongside the existing Socket.IO broadcast keeps Grafana clients in sync with the frontend and happens before the DynamoDB insert, which is a sensible order. If you ever want DB writes to proceed even when broadcasting fails, consider wrapping each broadcast in its own try/catch (or running them independently) so a transient WS/socket error can’t block insertLapData.

packages/server/src/datasources/WebSocket/WebSocket.types.ts (1)

5-9: Align WebSocketType method return types with call‑site usage

broadcastLapData and broadcastPacket are typed as returning void, but call sites like LapController.handleLapData currently await them. That works at runtime (the calls are synchronous), but it’s a bit misleading for readers and type‑checking.

Consider either:

  • Changing these signatures to Promise<void> and marking implementations async, or
  • Keeping them synchronous and dropping the unnecessary await at the call sites.

Either option keeps behavior the same but makes intent clearer.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 83cd549 and 456d971.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (9)
  • README.md (1 hunks)
  • docs/GRAFANA.md (1 hunks)
  • packages/server/.env.example (1 hunks)
  • packages/server/package.json (2 hunks)
  • packages/server/src/controllers/BackendController/BackendController.ts (2 hunks)
  • packages/server/src/controllers/BackendController/BackendController.types.ts (2 hunks)
  • packages/server/src/controllers/LapController/LapController.ts (2 hunks)
  • packages/server/src/datasources/WebSocket/WebSocket.ts (1 hunks)
  • packages/server/src/datasources/WebSocket/WebSocket.types.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/server/src/controllers/BackendController/BackendController.ts
  • packages/server/src/controllers/BackendController/BackendController.types.ts
  • README.md
  • packages/server/package.json
🧰 Additional context used
🧬 Code graph analysis (2)
packages/server/src/datasources/WebSocket/WebSocket.types.ts (1)
packages/shared/src/types.ts (2)
  • ILapData (421-437)
  • ITelemetryData (399-399)
packages/server/src/datasources/WebSocket/WebSocket.ts (4)
packages/server/src/utils/logger.ts (1)
  • createLightweightApplicationLogger (98-102)
packages/server/src/datasources/WebSocket/WebSocket.types.ts (1)
  • WebSocketType (5-9)
packages/server/src/controllers/BackendController/BackendController.ts (1)
  • BackendController (19-100)
packages/shared/src/types.ts (2)
  • ITelemetryData (399-399)
  • ILapData (421-437)
🪛 dotenv-linter (4.0.0)
packages/server/.env.example

[warning] 8-8: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 9-9: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 10-10: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 10-10: [UnorderedKey] The DRIVER_TABLE_NAME key should go before the LAP_TABLE_NAME key

(UnorderedKey)


[warning] 11-11: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 11-11: [UnorderedKey] The GPS_CALCULATED_LAP_DATA_TABLE key should go before the LAP_TABLE_NAME key

(UnorderedKey)


[warning] 12-12: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 12-12: [UnorderedKey] The GPS_TABLE_NAME key should go before the LAP_TABLE_NAME key

(UnorderedKey)


[warning] 25-25: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 26-26: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 26-26: [UnorderedKey] The LOKI_PASSWORD key should go before the LOKI_USERNAME key

(UnorderedKey)


[warning] 30-30: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 35-35: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 36-36: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 36-36: [UnorderedKey] The MQTT_PASSWORD key should go before the MQTT_USERNAME key

(UnorderedKey)


[warning] 41-41: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 42-42: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 47-47: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 48-48: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 50-50: [UnorderedKey] The LOG_ERROR_STACK key should go before the LOG_FILE key

(UnorderedKey)


[warning] 56-56: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 56-56: [UnorderedKey] The ENV key should go before the LOG_DIR key

(UnorderedKey)

🪛 markdownlint-cli2 (0.18.1)
docs/GRAFANA.md

3-3: Bare URL used

(MD034, no-bare-urls)


70-70: Link fragments should be valid

(MD051, link-fragments)


71-71: Link fragments should be valid

(MD051, link-fragments)


72-72: Link fragments should be valid

(MD051, link-fragments)


73-73: Link fragments should be valid

(MD051, link-fragments)


74-74: Link fragments should be valid

(MD051, link-fragments)


75-75: Link fragments should be valid

(MD051, link-fragments)


76-76: Link fragments should be valid

(MD051, link-fragments)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/server/.env.example (1)

58-61: Add a trailing newline at the end of the file.

Standard practice for text files is to end with a newline character. Also, consider wrapping the GRAFANA_WS_PATH value in quotes for consistency with other environment variables in the file, e.g., GRAFANA_WS_PATH="/grafana-ws".

Apply this diff:

 ########################
 # Grafana WebSocket    #
 ########################
-GRAFANA_WS_PATH=/grafana-ws
\ No newline at end of file
+GRAFANA_WS_PATH="/grafana-ws"
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5918dc6 and 6d2c984.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (1)
  • packages/server/.env.example (1 hunks)
🧰 Additional context used
🪛 dotenv-linter (4.0.0)
packages/server/.env.example

[warning] 8-8: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 9-9: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 10-10: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 10-10: [UnorderedKey] The DRIVER_TABLE_NAME key should go before the LAP_TABLE_NAME key

(UnorderedKey)


[warning] 11-11: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 11-11: [UnorderedKey] The GPS_CALCULATED_LAP_DATA_TABLE key should go before the LAP_TABLE_NAME key

(UnorderedKey)


[warning] 12-12: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 12-12: [UnorderedKey] The GPS_TABLE_NAME key should go before the LAP_TABLE_NAME key

(UnorderedKey)


[warning] 25-25: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 26-26: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 26-26: [UnorderedKey] The LOKI_PASSWORD key should go before the LOKI_USERNAME key

(UnorderedKey)


[warning] 30-30: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 35-35: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 36-36: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 36-36: [UnorderedKey] The MQTT_PASSWORD key should go before the MQTT_USERNAME key

(UnorderedKey)


[warning] 41-41: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 42-42: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 47-47: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 48-48: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 50-50: [UnorderedKey] The LOG_ERROR_STACK key should go before the LOG_FILE key

(UnorderedKey)


[warning] 56-56: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 56-56: [UnorderedKey] The ENV key should go before the LOG_DIR key

(UnorderedKey)


[warning] 61-61: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)

🔇 Additional comments (5)
packages/server/.env.example (5)

1-12: File structure and Database section look good.

The header comments clearly explain the file's purpose, and the Database section is well-organized. The QuoteCharacter warnings from dotenv-linter are false positives—quotes around placeholder values are intentional and standard practice in .env.example files to indicate string values.


14-30: Loki section is well-documented.

The authentication options are clearly explained, and the LOKI_LABELS format is helpful. This section provides good guidance for developers configuring logging.


32-42: MQTT and App secrets sections are appropriate.

The MQTT section provides default credentials (intended for local development), while App secrets use placeholders. This mixed approach is reasonable for development convenience.


44-50: Local logging section uses sensible defaults.

The logging configuration is clear and developer-friendly, with helpful comments for the LOG_LEVEL option.


52-56: Node/Environment section is correct.

Both NODE_ENV and ENV are appropriately set to "development" for this example.

@dastine0308 dastine0308 force-pushed the TEL-313-Setup-Websocket-connection-between-Grafana-and-Backend-Helios-Telemetry branch from 6d2c984 to 55b763f Compare November 29, 2025 17:52
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/server/src/controllers/BackendController/BackendController.ts (1)

92-99: Add WebSocket server cleanup.

The cleanup method should close the WebSocket server to ensure all connections are properly terminated and resources are released during shutdown.

   public async cleanup() {
     try {
       await this.databaseManager.close();
       logger.info("Database connection closed successfully");
     } catch (error) {
       logger.error("Error closing database connection:", error);
     }
+
+    try {
+      this.webSocket.wss.close(() => {
+        logger.info("WebSocket server closed successfully");
+      });
+    } catch (error) {
+      logger.error("Error closing WebSocket server:", error);
+    }
   }
♻️ Duplicate comments (2)
docs/GRAFANA.md (2)

69-71: Add language specifier to fenced code block.

This issue was previously flagged but remains unresolved. The fenced code block is missing a language identifier for proper syntax highlighting in rendered markdown.

Apply this diff:

-```
+```text
 http://localhost:3000

---

`86-88`: **Replace hard tabs with spaces for markdown compatibility.**

This issue was previously flagged but remains unresolved. Lines 86–88 use hard tabs for indentation. Markdown linters and renderers work best with spaces. Convert tabs to spaces (2 or 4 spaces per indent level).

Apply this diff:

```diff
-	- Click **Explore** in the left-hand menu.
-	- For **Data source**, choose **WebSocket API** (the WebSocket data source in the Telemetry-Visualizer setup).
-	- In the **Field** input enter: `$.data`
+  - Click **Explore** in the left-hand menu.
+  - For **Data source**, choose **WebSocket API** (the WebSocket data source in the Telemetry-Visualizer setup).
+  - In the **Field** input enter: `$.data`
🧹 Nitpick comments (3)
packages/server/.env.example (2)

58-61: Add documentation comment for GRAFANA_WS_PATH.

The new GRAFANA_WS_PATH variable lacks an explanatory comment describing its purpose and how to configure it. Following the pattern used for other environment variables (e.g., LOKI_URL at line 18), add a comment explaining what this WebSocket path is used for.

Additionally, consider whether the default value /grafana-ws should be provided, or whether this should be an empty string placeholder (like LOKI_URL) to encourage explicit configuration.

 ########################
 # Grafana WebSocket    #
 ########################
+# The WebSocket path used to broadcast telemetry and lap data to Grafana.
+# This must match the path configured in your Grafana instance.
-GRAFANA_WS_PATH=/grafana-ws
+GRAFANA_WS_PATH="" # e.g. "/grafana-ws"

Alternatively, if /grafana-ws is the recommended/standard default, keep the current value and just add the comment above it.


61-61: Add trailing newline at end of file.

The file is missing a blank line at the end, which is a common style convention. Add a trailing newline after line 61.

 GRAFANA_WS_PATH=/grafana-ws
+
packages/server/src/controllers/BackendController/BackendController.ts (1)

32-32: Add error handling for WebSocket initialization.

If the WebSocket server fails to start (e.g., port conflict, invalid configuration), the constructor will throw and crash the server. Consider wrapping this in try-catch similar to the database initialization pattern used in initializeDatabase().

     this.dynamoDB = new DynamoDB(this);
     this.socketIO = new SocketIO(httpsServer, this);
-    this.webSocket = new NativeWebSocket(grafanaWsPath, httpsServer, this);
+    try {
+      this.webSocket = new NativeWebSocket(grafanaWsPath, httpsServer, this);
+    } catch (error) {
+      logger.error("Failed to initialize WebSocket:", error);
+      throw error; // or handle gracefully depending on criticality
+    }
     this.mqtt = new SolarMQTTClient(options, this);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6d2c984 and 55b763f.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (9)
  • README.md (1 hunks)
  • docs/GRAFANA.md (1 hunks)
  • packages/server/.env.example (1 hunks)
  • packages/server/package.json (2 hunks)
  • packages/server/src/controllers/BackendController/BackendController.ts (2 hunks)
  • packages/server/src/controllers/BackendController/BackendController.types.ts (2 hunks)
  • packages/server/src/controllers/LapController/LapController.ts (2 hunks)
  • packages/server/src/datasources/WebSocket/WebSocket.ts (1 hunks)
  • packages/server/src/datasources/WebSocket/WebSocket.types.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • README.md
  • packages/server/src/controllers/LapController/LapController.ts
  • packages/server/src/datasources/WebSocket/WebSocket.types.ts
  • packages/server/package.json
  • packages/server/src/datasources/WebSocket/WebSocket.ts
🧰 Additional context used
🧬 Code graph analysis (1)
packages/server/src/controllers/BackendController/BackendController.types.ts (1)
packages/server/src/datasources/WebSocket/WebSocket.ts (1)
  • NativeWebSocket (14-88)
🪛 dotenv-linter (4.0.0)
packages/server/.env.example

[warning] 8-8: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 9-9: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 10-10: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 10-10: [UnorderedKey] The DRIVER_TABLE_NAME key should go before the LAP_TABLE_NAME key

(UnorderedKey)


[warning] 11-11: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 11-11: [UnorderedKey] The GPS_CALCULATED_LAP_DATA_TABLE key should go before the LAP_TABLE_NAME key

(UnorderedKey)


[warning] 12-12: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 12-12: [UnorderedKey] The GPS_TABLE_NAME key should go before the LAP_TABLE_NAME key

(UnorderedKey)


[warning] 25-25: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 26-26: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 26-26: [UnorderedKey] The LOKI_PASSWORD key should go before the LOKI_USERNAME key

(UnorderedKey)


[warning] 30-30: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 35-35: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 36-36: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 36-36: [UnorderedKey] The MQTT_PASSWORD key should go before the MQTT_USERNAME key

(UnorderedKey)


[warning] 41-41: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 42-42: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 47-47: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 48-48: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 50-50: [UnorderedKey] The LOG_ERROR_STACK key should go before the LOG_FILE key

(UnorderedKey)


[warning] 56-56: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 56-56: [UnorderedKey] The ENV key should go before the LOG_DIR key

(UnorderedKey)


[warning] 61-61: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)

🪛 markdownlint-cli2 (0.18.1)
docs/GRAFANA.md

69-69: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


86-86: Hard tabs
Column: 1

(MD010, no-hard-tabs)


87-87: Hard tabs
Column: 1

(MD010, no-hard-tabs)


88-88: Hard tabs
Column: 1

(MD010, no-hard-tabs)

🔇 Additional comments (3)
packages/server/src/controllers/BackendController/BackendController.types.ts (1)

6-6: LGTM!

The type additions correctly reflect the new WebSocket integration and follow the established pattern for other datasources.

Also applies to: 18-18

packages/server/src/controllers/BackendController/BackendController.ts (2)

10-10: LGTM!

The import and environment configuration for the Grafana WebSocket path are correctly implemented with a sensible default.

Also applies to: 16-16


74-76: Correctly commented out per team decision.

The commented-out broadcast is intentional, as the team decided to only send ILapData through WebSocket (handled in LapController), while Grafana receives ITelemetryData packets via MQTT.

Based on past review comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants