-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathconfig.yaml
More file actions
323 lines (308 loc) · 20 KB
/
Copy pathconfig.yaml
File metadata and controls
323 lines (308 loc) · 20 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
# TeslaUSB Configuration File
#
# EDIT THIS FILE to match your installation.
# This file is used by both bash scripts and Python web application.
#
# All paths, settings, and credentials are defined here as a single source of truth.
# ============================================================================
# Installation & Paths
# ============================================================================
installation:
target_user: pi # Linux user running the TeslaUSB services (auto-overridden by SUDO_USER during setup)
mount_dir: /mnt/gadget # Mount directory for USB drives
# ============================================================================
# Disk Images
# ============================================================================
disk_images:
cam_name: usb_cam.img # TeslaCam disk image filename
lightshow_name: usb_lightshow.img # LightShow disk image filename
cam_label: TeslaCam # Filesystem label for TeslaCam drive
lightshow_label: Lightshow # Filesystem label for LightShow drive
music_name: usb_music.img # Music disk image filename (optional third LUN)
music_label: Music # Filesystem label for Music drive (Tesla expects FAT32)
music_enabled: true # Create and present music partition (LUN2)
music_fs: fat32 # Filesystem for music image (fat32 recommended for Tesla)
boot_fsck_enabled: true # Auto-repair filesystems on boot (recommended)
# ============================================================================
# Setup Configuration (used only by setup_usb.sh)
# ============================================================================
# These values are specific to the setup process and can be configured here
# or left empty ("") for interactive prompts during setup.
setup:
part1_size: "" # TeslaCam drive size (e.g., "50G" or leave empty for interactive)
part2_size: "" # LightShow drive size (e.g., "10G" or leave empty for interactive)
part3_size: "" # Music drive size (e.g., "32G" or leave empty to skip/interactive)
reserve_size: "" # Headroom to leave free on Pi filesystem (default: 5G)
archive_reserve_size: "50G" # Space reserved for RecentClips archive on SD card
# ============================================================================
# Network & Security
# ============================================================================
network:
samba_password: tesla # Samba password for authenticated user (CHANGE THIS!)
web_port: 80 # Web interface port (80 required for captive portal - do not change)
# ============================================================================
# Offline Access Point Configuration
# ============================================================================
# Fallback WiFi access point for in-car/mobile access when STA WiFi is unavailable
offline_ap:
enabled: true # Set to false to disable fallback AP
interface: wlan0 # WiFi interface used for AP/STA
ssid: TeslaUSB # SSID broadcast when AP is active (CHANGE THIS!)
passphrase: teslausb1234 # WPA2 passphrase 8-63 chars (CHANGE THIS!)
channel: 6 # 2.4GHz channel (1-11)
ipv4_cidr: 192.168.4.1/24 # Static IP for AP interface
dhcp_start: 192.168.4.10 # DHCP range start
dhcp_end: 192.168.4.50 # DHCP range end
check_interval: 20 # Seconds between health checks
disconnect_grace: 30 # Seconds offline before starting AP
min_rssi: -70 # Minimum RSSI (dBm) to tear down AP
stable_seconds: 20 # Seconds of good link before stopping AP
ping_target: 8.8.8.8 # Ping target to confirm WAN reachability
retry_seconds: 300 # While AP is active, retry STA join every N seconds
virtual_interface: uap0 # Virtual AP interface name (concurrent mode always enabled)
force_mode: auto # Persistent force mode: auto, force_on, force_off
# ============================================================================
# System Configuration File Paths
# ============================================================================
system:
config_file: /boot/firmware/config.txt # Raspberry Pi boot configuration
samba_conf: /etc/samba/smb.conf # Samba configuration file
# ============================================================================
# Web Application Configuration
# ============================================================================
web:
secret_key: CHANGE-THIS-TO-A-RANDOM-SECRET-KEY-ON-FIRST-INSTALL # Flask secret key (auto-generated if default)
max_lock_chime_size: 1048576 # 1 MiB (1024 * 1024 bytes)
max_lock_chime_duration: 10.0 # 10 seconds (configurable per Tesla model)
min_lock_chime_duration: 0.3 # 300ms minimum
speed_range_min: 0.5 # Half speed (audio trimmer)
speed_range_max: 2.0 # Double speed (audio trimmer)
speed_step: 0.05 # Fine-grained control (audio trimmer)
lock_chime_filename: LockChime.wav # Active lock chime filename
chimes_folder: Chimes # Folder on part2 where custom chimes are stored
lightshow_folder: LightShow # Folder on part2 where light shows are stored
max_upload_size_mb: 2048 # Max accepted upload size for music/lightshow (MiB)
max_upload_chunk_mb: 16 # Chunk size for streaming uploads (MiB)
# ============================================================================
# Mapping & Geo-Indexing Configuration
# ============================================================================
mapping:
enabled: true # Enable GPS/telemetry indexing of dashcam videos
sample_rate: 30 # Extract every Nth frame (30 = ~1/sec at 30fps)
trip_gap_minutes: 5 # Gap between waypoints to start a new trip
# Phase 5.9 (issue #102) — exposed via Settings → Advanced.
index_too_new_seconds: 120 # Skip indexing for clips whose mtime is fresher than this many seconds (Tesla still writing)
event_detection:
harsh_brake_threshold: -4.0 # m/s² longitudinal (negative = braking)
emergency_brake_threshold: -7.0 # m/s² longitudinal
hard_accel_threshold: 3.5 # m/s² longitudinal (positive = accelerating)
sharp_turn_lateral_g: 4.0 # m/s² lateral
speed_limit_mps: 35.76 # ~80 mph speed alert threshold (0 = disabled)
# ============================================================================
# Cloud Archive Configuration
# ============================================================================
# Auto-sync dashcam footage to cloud storage via rclone
cloud_archive:
enabled: true
# Cloud provider (configured via web UI — credentials stored encrypted, NOT here)
provider: "" # google_drive | onedrive | dropbox | s3 | b2 | wasabi
remote_path: "TeslaUSB" # Path/folder on cloud storage
# Sync behavior
sync_folders: # Which TeslaCam subfolders to sync
- SentryClips
- SavedClips
- ArchivedClips # SD-card-resident copy of clips (RecentClips rotates hourly, so we sync the archived snapshot instead)
priority_order: # Sync priority (first = highest, user-configurable)
- SentryClips
- SavedClips
- ArchivedClips
max_upload_mbps: 5 # Bandwidth limit (Pi has limited upload)
cloud_reserve_gb: 1 # Keep this much free on cloud (don't fill to 100%)
keep_local_after_upload: true # Keep local files after uploading to cloud
sync_non_event_videos: false # Opt-in: sync videos without events or geolocation
cloud_auto_cleanup: false # Auto-delete old cloud videos when storage is low
cloud_cleanup_threshold_pct: 90 # Delete when cloud usage exceeds this %
cloud_min_retention_days: 30 # Keep at least this many days of videos
# Phase 2c (issue #76) — local ArchivedClips storage management on the SD card.
# The retention prune walks ~/ArchivedClips and deletes *.mp4 files older than
# this many days; trip/waypoint/event rows in geodata.db are preserved (only
# their video_path pointer is nulled). Disk-space guard refuses new copies and
# logs CRITICAL when the SD card free space drops below disk_space_critical_mb.
archived_clips_retention_days: 30 # Local SD-card retention for ArchivedClips/*.mp4
# Phase 2.6 — bulk cloud sync retry cap. After this many consecutive
# failures, a row moves from 'failed' to 'dead_letter' and is excluded
# from automatic re-picking. Failed rows can still be retried manually
# from the Failed Jobs page (Phase 4) or by manually setting status
# back to 'pending' in cloud_sync.db.
retry_max_attempts: 5 # 1-20; default 5
# Phase 1 item 1.3 — "retention respects cloud". Controls whether the
# retention prune is allowed to delete clips that have NOT yet been
# backed up to the cloud (status='synced' in cloud_synced_files.db).
# null → auto: keep un-uploaded clips when a provider is configured,
# age-only deletion otherwise (recommended default).
# false → "Keep clips until backed up" — protect un-uploaded clips
# past the retention cutoff. Surfaced as the toggle on
# Settings → Cloud Sync.
# true → age-only deletion (loses footage that was awaiting upload).
delete_unsynced: null
disk_space_warning_mb: 500 # Log WARNING when SD-card free space drops below this
disk_space_critical_mb: 100 # Refuse new archive copies when free space drops below this
disk_space_pause_seconds: 300 # How long the worker stays paused after a disk-critical refusal
# Wave 4 (issue #184): unified ``pipeline_queue`` integration.
#
# The cloud worker now reads its work from the unified
# ``pipeline_queue`` (added in PR-A → PR-F3). The flags below are
# the cutover knobs. PR-F4 flipped them ON by default; existing
# installs upgrading from a pre-PR-F4 release will automatically
# adopt the unified pipeline on next service restart.
#
# ``enqueue_to_pipeline`` — PRODUCER hook. When true, every event
# discovered by ``_discover_events`` is also enqueued into
# ``pipeline_queue`` with ``stage='cloud_pending'`` (idempotent via
# the existing UNIQUE index). Default ``true`` (was ``false`` until
# PR-F4) — operators who need to roll back to the legacy disk-walk
# path can flip this off.
enqueue_to_pipeline: true
# ``shadow_pipeline_queue`` — OBSERVABILITY shim used during the
# cutover. Compares the legacy reader's first pick against the
# ``pipeline_queue`` top-N and logs a WARNING on disagreement.
# Default ``false`` (was ``true`` until PR-F4) — the reader is now
# the unified queue itself, so the comparison is a tautology.
shadow_pipeline_queue: false
# ``use_pipeline_reader`` — when true, the cloud worker claims its
# next upload from ``pipeline_queue.claim_next_for_stage(stage=
# 'cloud_pending')`` instead of disk-walking. PR-F3 added the
# reader; PR-F4 flips the default to ``true``. The legacy reader
# path is preserved behind ``false`` only as a one-release rollback
# safety net; it will be removed entirely in a follow-up.
use_pipeline_reader: true
# ============================================================================
# Storage & Retention Configuration (Phase 3a.2 — closes part of #98)
# ============================================================================
# Single source of truth for archive/clip retention policies. Replaces the
# legacy ``cleanup_config.json`` (auto-migrated on first boot) and
# the scattered ``archive.retention_days`` / ``cloud_archive.archived_clips_retention_days``
# values (which remain as backward-compat fallbacks).
#
# Surfaced in the web UI under Settings → Storage & Retention so users can
# manage retention without SSHing in. All changes take effect on the next
# retention pass (no service restart needed).
cleanup:
# 0 = inherit from the legacy fallback chain
# (cloud_archive.archived_clips_retention_days → archive.retention_days → 30).
# The startup migration seeds this from the legacy keys on first boot so
# existing customizations carry over without a behavior change. Saving from
# the Settings UI also writes a concrete value here, making this the unified
# source of truth from that point on.
default_retention_days: 0
free_space_target_pct: 10 # Try to keep at least this % of SD-card capacity free; aggressive prune below this
max_archive_size_gb: 0 # Hard cap on total ArchivedClips size; 0 = no cap (time-based retention only) — Phase 5 wires the cap into the prune
short_retention_warning_days: 7 # Warn in the UI when default retention drops below this
policies: # Per-folder overrides (empty dict = use defaults everywhere)
# Examples (uncomment to override the default for a specific folder):
# SentryClips:
# enabled: false
# retention_days: 90
# SavedClips:
# enabled: false
# retention_days: 365
# RecentClips:
# enabled: false
# retention_days: 30
# ArchivedClips:
# enabled: true
# retention_days: 30
# Wave 4 PR-F4 (issue #184): the standalone ``live_event_sync``
# configuration block has been removed. Live-event uploads (Sentry/
# Saved) are now first-class ``pipeline_queue`` rows enqueued by the
# file_watcher event.json hook at ``PRIORITY_LIVE_EVENT`` (= 0), so
# the unified cloud_archive worker picks them up before any bulk
# catch-up rows automatically. There is no separate enable flag,
# queue, worker, retry policy, or daily data cap — the
# ``cloud_archive`` retry/cap settings apply to live events too.
# (The previous ``live_event_sync.upload_scope: "event_minute"``
# camera-subset selection is gone; live events upload the full event
# directory, same as bulk events. The "minute-only" optimisation was
# undocumented in the public docs and almost no Tesla event spans
# more than a minute anyway.)
# ============================================================================
# RecentClips Archive Configuration
# ============================================================================
# Automatically copies RecentClips from the USB image (RO mount) to the Pi's
# SD card before Tesla's 1-hour circular buffer deletes them. Zero USB disruption.
archive:
enabled: true # Enable RecentClips archival
interval_minutes: 2 # How often to check for new clips (tighter = less data loss)
retention_days: 30 # Delete archived clips older than this
min_free_space_gb: 20 # Hard floor — stop archiving if SD card < this free. Issue #109: raised from 10 → 20 GiB. On a typical 470 GiB SD card, 10 GiB ≈ 2.1% free, which is well below the point at which ext4 free-block-group search starts thrashing and inflates per-write cost 10–100×, starving the watchdog daemon. 20 GiB ≈ 4.2% gives ext4 enough headroom to keep writes cheap.
max_size_gb: 0 # 0 = auto (use all available space minus reserved)
path: "" # Archive path (default: ~/ArchivedClips, set by web app)
# Stationary RecentClips skip-at-source is now unconditional (issue #184
# Wave 1). The archive worker peeks at each RecentClips candidate's SEI
# metadata; clips with no GPS-bearing SEI message are marked
# ``skipped_stationary`` instead of being copied. Sentry/Saved event
# clips are PRIORITY_EVENTS and never enter the skip path. The reclaim
# button on the Storage & Retention card still cleans up already-archived
# stationary RecentClips from before this default was in effect.
# ============================================================================
# Archive Queue Configuration (issue #76)
# ============================================================================
# Persistent SQLite-backed queue (in geodata.db) for the two-queue archive
# pipeline. Three producers populate the queue: the inotify file watcher,
# a periodic full-directory rescan, and a one-shot boot catch-up scan.
# The Phase 2b worker drains the queue one file at a time. The whole
# subsystem is unconditional now (issue #184 Wave 1) — disabling it left
# the gadget in a degraded state with no recourse for catch-up after an
# unclean shutdown, so the kill-switch was removed.
archive_queue:
rescan_interval_seconds: 60 # Cadence of the periodic full-dir rescan
worker_check_interval_seconds: 5 # Idle/empty-queue sleep between worker iterations
retry_max_attempts: 3 # OSError retries before a row goes to dead_letter
copy_chunk_bytes: 1048576 # Chunk size (1 MiB) for the temp+fsync+rename copy
# Phase 2c (issue #76) — health watchdog cadence.
watchdog_check_interval_seconds: 60 # Frequency of the archive-health watchdog tick
# SDIO-bus contention safeguards. The Pi Zero 2 W shares one SDIO
# controller between the SD card and the WiFi chip; aggressive
# archive copies can starve the kernel watchdog daemon and trigger
# a hardware reset. These knobs let the worker back off under load.
inter_file_sleep_seconds: 1.0 # Sleep between successful copies (was 0.25)
load_pause_threshold: 3.5 # Pause when 1-min loadavg exceeds this
load_pause_seconds: 30 # How long to sleep when load is high
boot_scan_defer_seconds: 30 # Producer waits this long after start before its first scan
# Mid-copy SDIO-contention safeguards (issue #104). The
# between-files load-pause guard above only fires at the iteration
# boundary; once a single _atomic_copy starts, nothing throttles it.
# Under sustained SDIO saturation a 25-50 MB clip copy can take
# 3-6 minutes, starving the userspace watchdog daemon long enough
# to trigger the BCM2835 hardware watchdog (90s timeout). These two
# knobs add per-chunk and per-file safety nets inside _atomic_copy.
chunk_pause_seconds: 0.25 # Sleep between chunks when 1-min loadavg > load_pause_threshold (issue #104 mitigation A)
per_file_time_budget_seconds: 60.0 # Abort + requeue (no attempts bump) any single copy taking longer than this (issue #104 mitigation B)
# Phase 5.9 (issue #102) — additional advanced tunables exposed via
# the Settings → Advanced sub-page so power users can tune without
# editing this file by SSH.
stable_write_age_seconds: 5.0 # Re-queue clips whose mtime is fresher than this many seconds (avoids copying mid-write)
recent_clips_stable_write_age_seconds: 90.0 # Higher threshold for RecentClips (Tesla writes ~60s segments; moov atom appended at close — see archive_worker._CopyMoovIncomplete)
peek_give_up_age_seconds: 300.0 # If SEI peek raises a parser error AND file mtime is older than this, treat as stationary (skip) instead of falling through to copy — prevents queue cascade when VFS page cache holds a stale view of files Tesla wrote via the gadget block layer
stale_claim_max_age_seconds: 600.0 # Reset 'claimed' rows older than this back to 'pending' on worker startup (recovers crashed/OOMed claims)
# Wave 4 PR-E (issue #184): shadow-mode validation of the unified
# ``pipeline_queue``. When enabled, the archive worker peeks at
# ``pipeline_queue`` immediately before each ``archive_queue`` claim
# and logs a WARNING if the two readers disagree on which row is
# next. Pure observability — no behavioural change. Used to verify
# the dual-write produces correct ordering against real production
# traffic before PR-F cuts over the actual reader. Disable to
# silence the (cheap) extra peek; default ``true`` so we get
# production validation data while it's still cheap to fix
# mismatches.
shadow_pipeline_queue: true
# Wave 4 PR-F1 (issue #184): unified-queue reader cutover.
# When enabled, the archive worker claims its next row from
# ``pipeline_queue`` (via ``claim_next_for_stage``) instead of from
# the legacy ``archive_queue``. The legacy table continues to receive
# state mirrors via the existing dual-write hooks so it stays a
# consistent shadow during the cutover. Default ``false`` so a fresh
# deploy is a no-op; flip to ``true`` after PR-E shadow telemetry
# confirms the two readers agree on production traffic. Reverting is
# a single config-flag flip — no DB rollback needed.
use_pipeline_reader: false