# Tesla Light Show — Administrator & Developer Guide

Tesla Light Show is an official open-source content repository and validation toolkit from Tesla Motors. It enables owners and creators to design, validate, and deploy custom light shows on Tesla vehicles. See the User Guide for day-to-day usage; this guide covers architecture, the FSEQ format specification, validator internals, and contributor guidance.

---

## 1. Architecture Overview

This repository is a **content distribution repository**, not a runtime service. There is no server, no API, and no daemon process.

```text
Repository components:

  validator.py              — FSEQ validation tool (Python 3.7+, stdlib only)
  validator-windows.exe     — Pre-compiled Windows binary
  validator-macos.zip       — Pre-compiled macOS binary

  xlights/
    tesla_xlights_show_folder.zip          — xLights project: standard single-car layout
    tesla_xlights_cross_vehicle_folder.zip — xLights project: multi-car coordination layout

  examples/
    lightshow_example_1_elevator_music.zip              (1-car)
    lightshow_example_2_*.zip                           (1-car)
    lightshow_example_3_The_Arrival_5_Car/              (5-car, MP3 audio)
    lightshow_example_4_Ready_for_Assault_8_Car/        (8-car, WAV audio)
    lightshow_example_5_Cyber_Symphony_4_Car/           (4-car, WAV audio)
    lightshow_example_6_*.zip                           (1-car)
    lightshow_example_7_*.zip                           (1-car)

  cross-vehicle-shows/
    README.md               — Multi-car setup guide

  images/                   — Screenshots used in documentation
```

> [!NOTE]
> All runtime complexity lives off-repository: in the vehicle firmware, in xLights (third-party), and on the USB drive. This repository's software surface is exactly one Python file (`validator.py`, 48 lines) plus pre-compiled binaries built from it.

### Technology Stack

| Component | Technology | Notes |
|---|---|---|
| Validator | Python 3.7+, stdlib only | `dataclasses`, `struct`, `sys`, `datetime` |
| Sequence format | FSEQ v2.0 / v2.2 | Binary; produced by xLights |
| Audio formats | MP3, WAV | Loaded from USB alongside `.fseq` |
| Sequencing tool | xLights (third-party) | Produces `.fseq` and `.xsq` files |
| Delivery medium | USB FAT32 drive | `LightShow/` folder at root |

### Data Flow

```text
Creator:
  xLights (Tesla show folder project)
    → lightshow.fseq  +  lightshow.mp3 / .wav
    → validator.py  →  pass / ValidationError
    → USB LightShow/ folder
    → Tesla vehicle reads and plays show
```

---

## 2. System Requirements

| Item | Requirement |
|---|---|
| Python | 3.7 or later |
| Platform (Python) | Windows, macOS, Linux |
| Platform (pre-built) | Windows (`validator-windows.exe`), macOS (`validator-macos.zip`) |
| xLights | Any version producing FSEQ v2.0 or v2.2 uncompressed output |
| USB | FAT32, any capacity sufficient for `.fseq` + audio |
| Dependencies | None (standard library only) |

---

## 3. Configuration Reference

### FSEQ File Format Requirements

The validator reads a fixed-length binary header. All checks are against this header:

| Byte Offset | Size | Struct Format | Field | Required Value | Error if Violated |
|---|---|---|---|---|---|
| 0 | 4 bytes | `bytes` | magic | `b'PSEQ'` | `Unknown file format, expected FSEQ v2.0` |
| 4 | 2 bytes | `<H` | start | ≥ 24 | `Unknown file format, expected FSEQ v2.0` |
| 6 | 1 byte | `<B` | minor | 0 or 2 | Warning only |
| 7 | 1 byte | `<B` | major | 2 | Warning only |
| 10 | 4 bytes | `<I` | channel_count | 48 or 200 | `Expected 48 or 200 channels, got {N}` |
| 14 | 4 bytes | `<I` | frame_count | ≥ 1 | `Unknown file format, expected FSEQ v2.0` |
| 18 | 1 byte | `<B` | step_time | ≥ 15 | `Unknown file format, expected FSEQ v2.0` |
| 20 | 1 byte | `<B` | compression_type | 0 | `Expected file format to be V2 Uncompressed` |

Duration is computed as `(frame_count * step_time / 1000)` seconds and must be ≤ 14400 (4 hours).

> [!IMPORTANT]
> The FSEQ version check (bytes 6–7) produces a **warning message only** — it does not cause `sys.exit(1)`. A file with an unrecognized version will pass validation and can be loaded on the vehicle for testing. Only confirmed incompatibility warrants blocking.

### Channel Count by Vehicle

| Channel Count | Vehicle |
|---|---|
| 48 | Model S, Model 3, Model X, Model Y |
| 200 | Cybertruck |

The specific channel-to-light-zone mapping is defined in the xLights show folder projects, not in `validator.py`. To inspect or modify the mapping, open the relevant xLights project from `xlights/`.

### USB Directory Structure

```text
USB root/
  LightShow/
    lightshow.fseq     (required)
    lightshow.mp3      (optional — audio)
    lightshow.wav      (optional — audio, alternative to .mp3)
```

The multi-car example structure mirrors this per-car:

```text
lightshow_example_3_The_Arrival_5_Car/
  Car #1/
    LightShow/
      lightshow.fseq
      lightshow.mp3
      lightshow.xsq    (xLights project — not required on USB, reference only)
  Car #2/
    LightShow/
      ...
```

---

## 4. Deployment

### Single-Car Deployment Checklist

- [ ] Create sequence in xLights using `tesla_xlights_show_folder.zip` project
- [ ] Export as FSEQ (uncompressed, correct channel count for your vehicle)
- [ ] Run `validator.py` — confirm no errors
- [ ] Format USB drive as FAT32
- [ ] Create `LightShow/` at USB root
- [ ] Copy `lightshow.fseq` (and optional audio) to `LightShow/`
- [ ] Insert USB into vehicle
- [ ] Test show from vehicle UI

### Multi-Car Deployment

Each car requires its own USB drive with that car's sequence:

1. Open the multi-car xLights project from `xlights/tesla_xlights_cross_vehicle_folder.zip`.
2. Export a separate `.fseq` for each car's channel assignment.
3. Validate each `.fseq` individually.
4. Prepare one USB per car, each with its own `LightShow/lightshow.fseq`.
5. Load all USBs simultaneously; trigger all shows at the same moment.

> [!NOTE]
> The `.xsq` files present in multi-car examples (e.g. example 3) are xLights project files included for reference and remixing. They are **not** read by the vehicle — only `.fseq` and audio files are required on the USB drive.

---

## 5. Operations

### Validator CLI Reference

```bash
# Validate by path argument
python3 validator.py lightshow.fseq

# Interactive mode (prompts for drag-and-drop)
python3 validator.py
```

**Successful output:**
```text
Found {frame_count} frames, step time of {step_time} ms for a total duration of {H:MM:SS}.
```

**Exit codes:**

| Exit Code | Meaning |
|---|---|
| `0` | Validation passed (with or without version warning) |
| `1` | `ValidationError` raised — file does not meet requirements |

> [!TIP]
> Interactive mode strips surrounding double-quotes from the input path (Windows drag-and-drop behavior) and leading/trailing spaces (macOS drag-and-drop behavior). On Linux, type or paste the path manually when prompted.

### Binary Maintenance

When `validator.py` logic changes, the pre-compiled binaries must be rebuilt:

1. Update `validator.py`.
2. Rebuild `validator-windows.exe` (e.g., via PyInstaller on Windows).
3. Rebuild `validator-macos.zip` (e.g., via PyInstaller on macOS).
4. Commit all three changes together.

> [!WARNING]
> The pre-compiled binaries can silently diverge from `validator.py` if only the Python source is updated. A user on Windows running `validator-windows.exe` would be validating against the old logic. Always rebuild binaries in the same commit as validator logic changes.

---

## 6. Security

`validator.py` opens the provided path in read-only binary mode (`open(file_path, "rb")`). It reads at most the first ~21 bytes of the file header and makes no network calls, writes no files, and spawns no subprocesses.

> [!CAUTION]
> The script does not sanitize the file path provided by the user. A path like `../../../../etc/passwd` will cause the script to open that file in read-only binary mode. Because the validator expects a binary FSEQ header at offset 0, it will immediately fail with `Unknown file format, expected FSEQ v2.0` and exit. No data is modified, but the file IS opened. For any automation pipeline that accepts paths from untrusted external sources, validate and canonicalize the path before passing it to the validator.

---

## 7. Troubleshooting

### Validator Passes But Car Does Not Play the Show

**Cause:** FSEQ version mismatch beyond the 2.0/2.2 validated range, or firmware incompatibility.

**Solution:**
1. Check for the version warning in validator output.
2. If a version warning was printed, download an older xLights release that produces v2.0:
   ```text
   https://github.com/smeighan/xLights/releases
   ```
3. Re-export, re-validate, and re-test.

> [!WARNING]
> A validator pass guarantees the file meets the format constraints the validator checks. It does not guarantee firmware compatibility for FSEQ versions outside the 2.0/2.2 validated set.

### Pre-Compiled Binary and Python Source Produce Different Results

**Cause:** The binaries were compiled from an older version of `validator.py`.

**Solution:**
1. Check the channel count acceptance logic in `validator.py` (line: `if channel_count != 48 and channel_count != 200`).
2. Rebuild the binary from the current source.

> [!NOTE]
> GitHub issue #85 described exactly this situation — the pre-compiled binary expected 48 channels only, while the Python source had already been updated to accept 200. Always use `validator.py` directly when in doubt about binary freshness.

---

## 8. Developer Internals

### Module Reference — `validator.py`

The entire software component of this repository. 48 lines, Python 3.7+, stdlib only.

#### Classes

| Class | Base | Fields | Purpose |
|---|---|---|---|
| `ValidationError` | `Exception` | — | Raised on any FSEQ constraint violation |
| `ValidationResults` | `dataclasses.dataclass` | `frame_count: int`, `step_time: int`, `duration_s: int` | Return value from `validate()` |

#### Functions

| Function | Signature | Returns | Raises |
|---|---|---|---|
| `validate` | `validate(file) -> ValidationResults` | `ValidationResults` on success | `ValidationError` on any constraint violation |

#### `validate()` — Detailed Read Sequence

```python
magic = file.read(4)                          # offset 0: magic bytes
start, minor, major = struct.unpack("<HBB", file.read(4))  # offset 4-7
file.seek(10)
channel_count, frame_count, step_time = struct.unpack("<IIB", file.read(9))  # offset 10-18
file.seek(20)
compression_type, = struct.unpack("<B", file.read(1))  # offset 20
```

Bytes 8–9 (offsets skipped by `seek(10)`) and bytes 19 (skipped by `seek(20)`) are present in the FSEQ v2.0 header but are not checked by this validator. These correspond to the sequence UUID and related fields in the FSEQ specification.

> [!NOTE]
> The `duration_s` field in `ValidationResults` is typed as `int` in the dataclass definition but receives the result of `frame_count * step_time / 1000`, which is a `float` in Python 3. The `timedelta` call in the error message handles this correctly; the field name `duration_s` implies seconds.

#### Main Block

The `__main__` block:

1. Reads `sys.argv[1]` if provided; otherwise calls `input()` for drag-and-drop.
2. Strips `"` surrounding quotes (Windows drag-and-drop adds these).
3. Strips leading/trailing spaces (macOS drag-and-drop adds these).
4. Opens file in binary read mode.
5. Calls `validate(file)`.
6. On success: prints `Found {frame_count} frames, step time of {step_time} ms for a total duration of {timedelta}.`
7. On `ValidationError`: prints the error message, calls `input("Press Enter to exit...")`, exits with code 1.
8. On success: calls `input("Press Enter to exit...")` before returning.

The `input("Press Enter to exit...")` pattern keeps the terminal window open after double-clicking the script or compiled binary on Windows/macOS, so users can read the result.

### Code Patterns and Conventions

| Pattern | Description |
|---|---|
| No external dependencies | `validator.py` imports only stdlib modules. Users should never need `pip install`. |
| Interactive fallback | No-argument invocation triggers `input()` for drag-and-drop — usable by non-technical users. |
| Fail-fast validation | Checks are ordered: format first, then channel count, then duration. The first violation raises immediately. |
| Binary-mode file reads | `open(..., "rb")` — safe for all OS line-ending handling, required for binary FSEQ parsing. |

### FSEQ Format Background

FSEQ (Falcon SEQuence) is a binary format originating from the Falcon Player project for holiday light sequencing. xLights produces FSEQ files compatible with Falcon Player. Tesla's subset restriction — exactly 48 or 200 channels, no compression, v2.0 or v2.2 — is enforced purely by `validator.py`. The format itself supports many more channel counts and compression options.

### Repository Structure Deep Dive

```text
teslamotors/light-show @ 9f94951
│
├── validator.py                     Core validation tool (48 lines)
├── validator-windows.exe            Pre-compiled binary (Windows)
├── validator-macos.zip              Pre-compiled binary (macOS)
│
├── xlights/
│   ├── tesla_xlights_show_folder.zip          Single-car channel layout
│   └── tesla_xlights_cross_vehicle_folder.zip Multi-car channel layout
│
├── cross-vehicle-shows/
│   └── README.md                    Car positioning + channel docs
│
├── examples/
│   ├── lightshow_example_1_elevator_music.zip          (1-car, ZIP)
│   ├── lightshow_example_2_*.zip                       (1-car, ZIP)
│   ├── lightshow_example_3_The_Arrival_5_Car/          (5-car folder)
│   │   ├── Car #1/LightShow/{fseq, mp3, xsq}
│   │   └── Car #2..#5/LightShow/{fseq, mp3, xsq}
│   ├── lightshow_example_4_Ready_for_Assault_8_Car/    (8-car folder)
│   │   └── Car #1..#8/LightShow/{fseq, wav}
│   ├── lightshow_example_5_Cyber_Symphony_4_Car/       (4-car folder)
│   │   └── Car #1..#4/LightShow/{fseq, wav}
│   ├── lightshow_example_6_*.zip                       (1-car, ZIP)
│   └── lightshow_example_7_*.zip                       (1-car, ZIP)
│
└── images/                          ~50 screenshots for documentation
```

### Testing

No automated test suite is included. Manual validation testing:

| Test Case | Expected Result |
|---|---|
| Known-good 48-channel `.fseq` | Frame count and duration printed; exit 0 |
| Known-good 200-channel `.fseq` | Frame count and duration printed; exit 0 |
| Wrong channel count | `Expected 48 or 200 channels, got {N}`; exit 1 |
| Compressed FSEQ | `Expected file format to be V2 Uncompressed`; exit 1 |
| Non-FSEQ file | `Unknown file format, expected FSEQ v2.0`; exit 1 |
| Duration > 4 hours | `Expected total duration to be less than 4 hours, got {duration}`; exit 1 |
| FSEQ v2.2 file | Warning printed; frame count and duration printed; exit 0 |

### Contributing

The repository accepts GitHub pull requests. Key contribution areas:

**Extending channel support (issue #141):**
1. Update the channel count check in `validator.py`: `if channel_count != 48 and channel_count != 200` → expand to additional supported values.
2. Create or update xLights show folder project(s) with the new channel layout.
3. Rebuild pre-compiled binaries for Windows and macOS.
4. Add or update example shows demonstrating the new channels.

**Adding new example shows:**
- Follow the folder structure in examples 3–5 for multi-car (per-car `LightShow/` folder with `lightshow.fseq` + audio).
- Validate all `.fseq` files before submitting.
- Include the `.xsq` source file for remixability.

---

_Generated May 2026 from teslamotors/light-show at 9f94951. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
