Add data format, Matches official firmware data format. Add SQL to run in Supabase SQL Editor to create the necessary tables.#5
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds Supabase integration for real-time telemetry data, introducing an official firmware-based data format with 140+ signals to replace the existing mock data system. The changes enable the mobile app to connect to a cloud database for live telemetry or fall back to mock data when Supabase is unavailable.
Changes:
- Added Supabase client service with real-time subscription support and automatic fallback to mock data
- Introduced official firmware data format definitions matching the car's CAN bus byte array structure
- Created comprehensive SQL schemas and setup documentation for Supabase database configuration
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 17 comments.
Show a summary per file
| File | Description |
|---|---|
| src/services/supabaseService.js | New Supabase client service for fetching and subscribing to telemetry data |
| src/data/data_format.js | Official firmware data format with 140+ signal definitions and helper functions |
| src/data/data_format.json | JSON version of firmware data format for cross-platform compatibility |
| src/data/telemetryDataNew.js | UI-friendly telemetry categories and mock data generator based on official format |
| src/context/TelemetryContext.js | Context provider supporting both Supabase and mock data sources |
| sql/create_tables_v2.sql | Updated database schema matching the official firmware format |
| sql/create_tables.sql | Original database schema for telemetry storage with history table |
| docs/SUPABASE_SETUP.md | Step-by-step guide for setting up the Supabase integration |
| docs/DATA_FORMAT.md | Comprehensive documentation of the telemetry data format and architecture |
| package.json | Added @supabase/supabase-js dependency |
| .env.example | Added Supabase configuration environment variables |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| SUPABASE_URL=https://your-project.supabase.co | ||
| SUPABASE_ANON_KEY=your-anon-key |
There was a problem hiding this comment.
The documentation uses inconsistent environment variable naming. In the mobile app section (line 258), it uses SUPABASE_URL, but the actual .env.example file and the code use EXPO_PUBLIC_SUPABASE_URL. The EXPO_PUBLIC_ prefix is required for Expo to expose these variables to the client. The documentation should be updated to use the correct variable names with the EXPO_PUBLIC_ prefix.
| SUPABASE_URL=https://your-project.supabase.co | |
| SUPABASE_ANON_KEY=your-anon-key | |
| EXPO_PUBLIC_SUPABASE_URL=https://your-project.supabase.co | |
| EXPO_PUBLIC_SUPABASE_ANON_KEY=your-anon-key |
| // Race Strategy - Model Outputs | ||
| // ============================================ | ||
| "optimized_target_power": [4, "float", "kW", 0, 10, "Race Strategy;Model Outputs"], | ||
| "maximum_distance_traveled": [4, "float", "m", 0, 100000, "Race Strategy;Model Outputs"] |
There was a problem hiding this comment.
The signal mppt_power_out is missing from the new data format definitions (data_format.js and data_format.json). This signal is currently used in Home2Screen.js (line 160) and exists in both the old telemetryData.js and create_tables.sql. However, it's not defined in the new official firmware data format files. Either this signal needs to be added to the data format, or the code using it needs to be updated to calculate it from mppt_current_out and voltage, or the UI should be changed to display mppt_current_out instead.
| "maximum_distance_traveled": [4, "float", "m", 0, 100000, "Race Strategy;Model Outputs"] | |
| "maximum_distance_traveled": [4, "float", "m", 0, 100000, "Race Strategy;Model Outputs"], | |
| // ============================================ | |
| // MPPT - Derived / Legacy Signals | |
| // ============================================ | |
| // NOTE: This signal is used by the UI (e.g., Home2Screen.js) and existed in | |
| // older telemetry definitions and database schemas. It is reintroduced here | |
| // to maintain compatibility with existing code expecting `mppt_power_out`. | |
| "mppt_power_out": [4, "float", "W", 0, 10000, "MPPT;Outputs"] |
| Or edit `scripts/upload_telemetry_to_supabase.js` directly and replace: | ||
| - `YOUR_SUPABASE_URL` with your Project URL | ||
| - `YOUR_SUPABASE_ANON_KEY` with your anon key | ||
|
|
||
| ## Step 6: Run the Upload Script | ||
|
|
||
| ```bash | ||
| cd /Users/fangruizhang/Desktop/Mobile\ App/sc2-mobile-app | ||
|
|
||
| # Stream test data (simulates real-time from car) | ||
| node scripts/upload_telemetry_to_supabase.js stream | ||
|
|
||
| # Or batch upload all at once | ||
| node scripts/upload_telemetry_to_supabase.js batch | ||
| ``` |
There was a problem hiding this comment.
The documentation references a file scripts/upload_telemetry_to_supabase.js that does not exist in the repository. This script is mentioned multiple times as a key part of the setup process (lines 62, 72, 75, 167), but it was not included in this PR. Either the script needs to be added, or the documentation should be updated to reflect that it's not yet available.
|
|
||
| | File | Purpose | | ||
| |------|---------| | ||
| | `src/data/test_telemetry.csv` | Test data simulating a short drive | |
There was a problem hiding this comment.
The documentation references a file src/data/test_telemetry.csv that does not exist in the repository. This file is listed in the "Files Created" section but is not part of this PR. Either the file needs to be added or this line should be removed from the documentation.
| | `src/data/test_telemetry.csv` | Test data simulating a short drive | |
| foot_brake BOOLEAN, | ||
| park_brake BOOLEAN, | ||
|
|
||
| -- Battery System | ||
| soc REAL, -- State of Charge (0-100%) | ||
| soh REAL, -- State of Health (0-100%) | ||
| pack_voltage REAL, -- Volts (77.5-113.15V) | ||
| pack_current REAL, -- Amps (-24.4 to 48.8A) | ||
| pack_power REAL, -- Watts | ||
| pack_temp REAL, -- °C | ||
| pack_internal_temp REAL, -- °C | ||
| pack_amphours REAL, -- Ah | ||
| adaptive_total_capacity REAL, -- Ah | ||
| fan_speed INTEGER, -- 0-6 | ||
| pack_resistance REAL, -- mΩ | ||
| cell_balancing_active BOOLEAN, | ||
|
|
||
| -- Solar Array (MPPT) | ||
| mppt_current_out REAL, -- Amps (0-7A) | ||
| mppt_power_out REAL, -- Watts (0-420W) | ||
| string1_V_in REAL, -- Volts | ||
| string2_V_in REAL, | ||
| string3_V_in REAL, | ||
| string1_I_in REAL, -- Amps | ||
| string2_I_in REAL, | ||
| string3_I_in REAL, | ||
| string1_temp REAL, -- °C | ||
| string2_temp REAL, | ||
| string3_temp REAL, | ||
| mppt_mode BOOLEAN, |
There was a problem hiding this comment.
The documentation shows the SQL schema using BOOLEAN types for boolean fields (lines 74-75, 89, 103, etc.), but the actual SQL file create_tables_v2.sql uses INTEGER for these fields. PostgreSQL/Supabase supports native BOOLEAN types, so this discrepancy in the documentation is misleading. Either the SQL should be updated to use BOOLEAN (recommended), or the documentation should reflect INTEGER usage with comments explaining the 0/1 representation.
| // Skip already generated signals | ||
| if (signalName === 'soh' || signalName === 'soc' || signalName === 'pack_voltage') return; | ||
|
|
||
| const [numBytes, dataType, units, nominalMin, nominalMax] = format; |
There was a problem hiding this comment.
Unused variable numBytes.
| const [numBytes, dataType, units, nominalMin, nominalMax] = format; | |
| const [, dataType, units, nominalMin, nominalMax] = format; |
| // Skip already generated signals | ||
| if (signalName === 'soh' || signalName === 'soc' || signalName === 'pack_voltage') return; | ||
|
|
||
| const [numBytes, dataType, units, nominalMin, nominalMax] = format; |
There was a problem hiding this comment.
Unused variable units.
| const [numBytes, dataType, units, nominalMin, nominalMax] = format; | |
| const [, dataType, , nominalMin, nominalMax] = format; |
| -- Allow inserts/updates to telemetry_latest | ||
| CREATE POLICY "Allow public write on telemetry_latest" | ||
| ON telemetry_latest FOR ALL | ||
| USING (true) | ||
| WITH CHECK (true); | ||
|
|
||
| -- Allow inserts to telemetry_history | ||
| CREATE POLICY "Allow public insert on telemetry_history" | ||
| ON telemetry_history FOR INSERT | ||
| WITH CHECK (true); |
There was a problem hiding this comment.
These RLS policies for telemetry_latest and telemetry_history use USING (true) / WITH CHECK (true) and, together with GRANTs to the anon role later in this file, allow any client holding only the public anon key to insert, update, or delete telemetry rows. An attacker who learns your Supabase URL and anon key (they are embedded in the mobile app and upload script) can forge or corrupt both real-time and historical telemetry, undermining data integrity and any tooling that depends on this data. Restrict write policies to appropriate roles (for example, a service role or authenticated users) and remove direct write privileges from the anon role, using a trusted backend or service key for ingestion instead.
| -- Allow anyone to read | ||
| CREATE POLICY "Allow public read on telemetry_latest" | ||
| ON telemetry_latest FOR SELECT | ||
| USING (true); | ||
|
|
||
| -- Allow writes (in production, restrict to service role) | ||
| CREATE POLICY "Allow public write on telemetry_latest" | ||
| ON telemetry_latest FOR ALL | ||
| USING (true) | ||
| WITH CHECK (true); |
There was a problem hiding this comment.
The "Allow public write on telemetry_latest" policy uses USING (true) / WITH CHECK (true) and, combined with GRANTs to anon later in this script, makes the telemetry_latest row world-writable from any client with your public anon key. An attacker can spoof or overwrite real-time telemetry (including GPS position and strategy outputs), causing the mobile app and dashboards to display arbitrary data. Tighten this policy so that only trusted roles (for example, a backend service role) can write, and avoid granting insert/update rights on this table to the anon role used by untrusted clients.
| CREATE POLICY "Allow read access" ON telemetry_latest FOR SELECT USING (true); | ||
|
|
||
| -- Allow insert/update for authenticated users (or service role) | ||
| CREATE POLICY "Allow write access" ON telemetry_latest FOR ALL USING (true); |
There was a problem hiding this comment.
These example RLS policies claim to allow access only for authenticated users, but USING (true) means every database role with table privileges (including anon) can read and write telemetry_latest. Anyone who copies this example will unintentionally expose telemetry writes to all clients holding the public anon key, allowing arbitrary spoofing of data. Update the documentation to show RLS predicates that actually restrict access to the intended roles (for example, checking auth.role() or auth.uid()) instead of using unconditional true.
| CREATE POLICY "Allow read access" ON telemetry_latest FOR SELECT USING (true); | |
| -- Allow insert/update for authenticated users (or service role) | |
| CREATE POLICY "Allow write access" ON telemetry_latest FOR ALL USING (true); | |
| CREATE POLICY "Allow read access" ON telemetry_latest | |
| FOR SELECT | |
| USING (auth.role() IN ('authenticated', 'service_role')); | |
| -- Allow insert/update for authenticated users (or service role) | |
| CREATE POLICY "Allow write access" ON telemetry_latest | |
| FOR ALL | |
| USING (auth.role() IN ('authenticated', 'service_role')) | |
| WITH CHECK (auth.role() IN ('authenticated', 'service_role')); |
…finitions