Settings Reference
Every key in settingsConfigure.json: type, default, allowed values, the UI page that owns it, and what it affects.
Overview
Settings are owned by SettingsManager (src/managers/settingsmanager.{h,cpp})
and persisted to settingsConfigure.json in the OS-specific config directory:
- Windows:
%APPDATA%/OCTAVE/settingsConfigure.json - macOS:
~/Library/Application Support/OCTAVE/settingsConfigure.json - Linux / Raspberry Pi / Orange Pi:
~/.config/OCTAVE/settingsConfigure.json(or$XDG_CONFIG_HOME/OCTAVE/) - Android / iOS: the Linux rule applied to
QDir::homePath()
buildDefaultSettings() defines 96 top-level default keys. Settings are read once at startup, validated against
the defaults, served from memory, and written back atomically 500 ms after the last change. The mechanics
are on the Settings Manager page; this page is the key catalogue.
How to read the tables:
- Property: the QML-visible property name (same as the JSON key) or how QML reaches a key that has no property.
- Type: the C++ property type, or for keys without a property the JSON type (string, object, list) as stored.
- Default: the literal in
buildDefaultSettings(). - Range / Values: what the save slot enforces (clamp or whitelist), and in parentheses the range the UI control offers when it is narrower. A dash means the slot stores whatever it is given.
- UI: the page under frontend/settings/ (Display, Media, OBD, Accessories, Device, About) or the other QML file that writes the key. See Settings UI.
buildSettingsRegistry()) and can be pinned to the Quick Panel. They
are marked pinnable in the description; the registry itself is documented on the
Settings Manager page.
Device and Window
| Property | JSON Key | Type | Default | Range / Values | UI | Description |
|---|---|---|---|---|---|---|
deviceName | deviceName | QString | "Default Device" | – | Device page (via Main.updateDeviceName(), which rejects blank names) | Display name for this OCTAVE installation |
fontSetting | fontSetting | QString | "GoogleSans Regular" | – | Display page | UI font family; Style.setFont() is called on change. The default is the display name Main.qml derives from the bundled GoogleSans-Regular.ttf; a stored legacy "Google Sans" (which Style.setFont() rejected, leaving the system font) is migrated to it at load and written back |
windowState | windowState | QString | "windowed" | "windowed", "fullscreen", "maximized" (others ignored) | Display page (Layout) | Window mode applied at startup |
screenWidth | screenWidth | int | 1280 | 320 – 7680 (clamped by the saver) | Main.qml on window resize (desktop only; the Display page's field resizes the window) | Window width in pixels when windowed |
screenHeight | screenHeight | int | 720 | 300 – 4320 (clamped by the saver) | Main.qml on window resize (desktop only) | Window height in pixels when windowed |
Display
| Property | JSON Key | Type | Default | Range / Values | UI | Description |
|---|---|---|---|---|---|---|
uiScale | uiScale | float | 1.0 | – (UI 0.5 – 2.0, step 0.05) | Display page | Global UI scale factor (Spacing.globalScale). A stored 0.6 (an old default) is migrated to 1.0 at load. Pinnable |
themeSetting | themeSetting | QString | "CosmicVoyager" | Any built-in or custom theme name; "Album Art Capture" enables palette extraction | Display page; Main.updateTheme() | Active colour theme (Theme System). Pinnable (chips from themeList) |
customThemes | customThemes | object (property customThemes is a QVariantList of names, customThemeData the whole map) | {} | name → theme object with colour keys and optional albumArt file URL | Saved from MediaRoom (save_custom_theme), deleted from the Display page | User-created themes captured from album art; art files live in theme_art/ |
colorTransitionMs | colorTransitionMs | int | 1000 | – (UI and registry 0 – 2000, step 50) | Display page | Duration of theme colour transitions in ms. Pinnable |
songLengthTransition | songLengthTransition | bool | false | true/false | Display page | Animate the theme colour transition over the whole song length instead of colorTransitionMs |
showClock | showClock | bool | true | true/false | Display page (Clock); Display widget | Show the clock in the bottom bar. Pinnable |
clockFormat24Hour | clockFormat24Hour | bool | true | true/false | Display page (Clock) | 24-hour clock (false = 12-hour). Signal is clockFormatChanged |
clockShowSeconds | clockShowSeconds | bool | false | true/false | Display page (Clock) | Show seconds in the bottom-bar clock |
clockSize | clockSize | int | 18 | – (UI 10 – 85, step 1) | Display page (Clock) | Manual clock font size in pixels |
clockSizeAuto | clockSizeAuto | bool | true | – | Display page (Clock) | Fit the clock to its slot in the nav bar (either orientation); when on, clockSize is ignored |
bottomBarOrientation | bottomBarOrientation | QString | "bottom" | "bottom", "side" (UI chips; slot does not validate) | Display page (Layout) | Navigation bar along the bottom edge or the side |
showBottomBarMediaControls | showBottomBarMediaControls | bool | false | true/false | Display page (Layout) | Play/skip controls inside the navigation bar. Pinnable |
settingsLayoutStyle | settingsLayoutStyle | QString | "Sidebar" | "Carousel", "Sidebar", "Hub", "Dashboard" (others ignored) | Display page (Layout) | Which Settings*Layout.qml renders the settings menu; "Sidebar" is the tile grid, shown as "Grid" in the UI |
albumArtColors | albumArtColors | QString (JSON) on disk; QVariantMap property | "" | Palette JSON produced by the extractor; the property is the parsed theme object (empty map when unset) | Not user-editable: written by MediaManager via set_album_art_colors() | Last extracted Album Art Capture palette, restored at startup so the placeholder theme does not flash |
Media and Now Playing
These keys are owned by the Media page (Library, Playback and Now Playing sub-sections). The Now Playing visual keys are additionally editable from frontend/NowPlayingStudio.qml with a live preview, and the three most common toggles appear on the Media widget.
| Property | JSON Key | Type | Default | Range / Values | UI | Description |
|---|---|---|---|---|---|---|
startUpVolume | startUpVolume | float | 0.1 | – (registry 0 – 1, step 0.01, logarithmic) | Media page (its slider runs 0–100 % and saves volumeController.toLinear()); Main.updateStartupVolume() | Linear gain applied at launch; currentVolume starts at round(sqrt(v) × 100) = 32 % for the default, which is also what the mute button restores at 0 %. Pinnable |
mediaFolder | mediaFolder | QString | "" (resolved at load to QStandardPaths::MusicLocation, falling back to ~/Music) | Any directory; saving the current value is a no-op | Media page (Library) | Root of the local music library; MediaManager connects mediaFolderChanged to set_library_root |
autoPlayOnStartup | autoPlayOnStartup | bool | false | true/false | Media page (Library & Playback); Media widget | Resume the last song at launch (not when Android Auto owned the music at shutdown: mediaLastSource). Pinnable |
persistShuffleState | persistShuffleState | bool | false | true/false | Media page (Library & Playback) | Restore lastShuffleState at launch. Pinnable |
musicButtonDefaultPage | musicButtonDefaultPage | QString | "mediaRoom" | "mediaRoom", "mediaPlayer" (others ignored) | Media page (Library & Playback) | Which view the music nav button opens |
returnToLibraryAfterSelection | returnToLibraryAfterSelection | bool | false | true/false | Media page (Library & Playback) | Keep the library open after selecting a track when true; false opens the Now Playing view |
showWaveformVisualizer | showWaveformVisualizer | bool | true | true/false | Media page; NowPlayingStudio; Media widget | FFT waveform in MediaRoom (Audio Analyzer). Pinnable |
show3DButtonTilt | show3DButtonTilt | bool | false | true/false | Media page; NowPlayingStudio | 3D tilt on prev/play/next press. Pinnable |
show3DAlbumPreview | show3DAlbumPreview | bool | false | true/false | Media page; NowPlayingStudio; Media widget | Coverflow-style side cards around the album art. Pinnable |
roundedAlbumArt | roundedAlbumArt | bool | true | true/false | Media page; NowPlayingStudio | Rounded corners on album art. Pinnable |
albumArtCornerRadius | albumArtCornerRadius | int | 16 | clamped 2 – 32 | Media page; NowPlayingStudio | Corner radius in dp when rounded. Pinnable |
showAlbumArtShadow | showAlbumArtShadow | bool | true | true/false | Media page; NowPlayingStudio | Drop shadow behind album art. Pinnable |
get_setting_with_default() / save_setting() | musicDeviceModel | string | "Album art" | "Album art", "Record player", "MP3 player", "CD player", "Cassette player", "iPod" (validated by MusicDevicePreference; SettingsManager checks only the string type) | Media page; NowPlayingStudio | Now Playing artwork or 3D music-device model. The 3D choices require the Quick3D scene to load; unknown stored choices display as "Album art" |
vinylRecordMode | vinylRecordMode | bool | false | true/false | Media page; NowPlayingStudio | Spin the album art like a record while playing. Pinnable |
audioOutputDevice | audioOutputDevice | QString | "" | a QAudioDevice id (up to 512 characters), or "" for the system default | Media page (Library & Playback) | The output music and phone audio play to. A picked output that is unplugged falls back to the system default until it is back |
albumArtTransition | albumArtTransition | QString | "Crossfade" | "Crossfade", "Slide", "Vinyl Lift", "Dissolve", "Flip", "Coverflow", "Conveyor", "Stack", "Depth", "Swing" (others ignored) | Media page; NowPlayingStudio | Track-change animation. With 3D preview on, the side cards animate along; off, only the centre card animates. Pinnable |
backgroundOverlayOpacity | backgroundOverlayOpacity | int | 80 | clamped 0 – 100 | Media page; NowPlayingStudio | Dark overlay percentage over the blurred background. Pinnable |
backgroundBlurRadius | backgroundBlurRadius | int | 40 | – (registry 0 – 100, step 1) | Media page; NowPlayingStudio | Blur radius of the album-art background. Pinnable |
backgroundGrid | backgroundGrid | QString | "4x4" | "Normal", "2x2", "4x4" (UI chips; slot does not validate) | Media page; NowPlayingStudio | Tiling of the album-art background |
sideCardOpacity | sideCardOpacity | double | 0.4 | clamped 0.1 – 1.0 | Media page; NowPlayingStudio | Opacity of the 3D preview side cards. Pinnable |
sideCardAngle | sideCardAngle | int | 30 | clamped 5 – 60 | Media page; NowPlayingStudio | Rotation of the side cards in degrees. Pinnable |
buttonTiltDuration | buttonTiltDuration | int | 200 | clamped 50 – 500 | Media page; NowPlayingStudio | Button tilt animation length in ms. Pinnable |
textScrollSpeed | textScrollSpeed | int | 5000 | clamped 1000 – 10000 | Media page; NowPlayingStudio | Marquee scroll duration in ms. Pinnable |
directoryHistory | directoryHistory | QVariantList (strings) | [] | Most recent first, max 10, no duplicates | No current writer (API kept for the folder picker) | Recently used media folders |
Playback Resume
Written by MediaManager, never by the settings UI. save_playback_state() stores the three lastPlayed* keys in one write from MediaManager's 1 s debounce and at shutdown.
| Property | JSON Key | Type | Default | Range / Values | Description |
|---|---|---|---|---|---|
lastShuffleState | lastShuffleState | bool | false | true/false | Shuffle state at last change; restored only when persistShuffleState is on |
lastPlayedSong | lastPlayedSong | QString | "" | Filename | Track to resume |
lastPlayedPosition | lastPlayedPosition | int | 0 | Milliseconds | Position to resume from |
mediaLastSource | mediaLastSource | QString | "local" | local / phone / androidauto | Which source owned the music when OCTAVE last ran (written by nowPlaying). androidauto with mediaLastPlaying resumes Android Auto on its next connect and skips local auto-play on startup |
mediaLastPlaying | mediaLastPlaying | bool | false | Whether that source was playing | |
lastPlayedPlaylist | lastPlayedPlaylist | QString | "" | Playlist name | Playlist active at last save |
OBD
| Property | JSON Key | Type | Default | Range / Values | UI | Description |
|---|---|---|---|---|---|---|
obdBluetoothPort | obdBluetoothPort | QString | "" (a stored "/dev/rfcomm0" is cleared at load) | Serial device path on desktop, or rfcomm://<MAC>[/<channel>] for a direct Bluetooth socket (Linux); BLE MAC address on Android | OBD page (Connection); also written by OBDManager when connecting | Adapter to connect to; OBDManager reconnects on change (OBD Manager) |
obdSavedAdapters | obdSavedAdapters | QString (JSON array) on disk; QVariantList property | "[]" | Array of {"name": string, "mac": string}; MAC upper-cased, most recent first, max 8. Stored as a string; the property is the parsed list of maps | OBD page (Adapters) removes; OBDManager::connect_to_adapter() adds | Remembered adapters shown as quick-connect chips |
obdFastMode | obdFastMode | bool | true | true/false | OBD page (Connection); OBD widget | Fast polling mode, applied on the next connection. Pinnable |
blackBoxEnabled | blackBoxEnabled | bool | true | true/false | OBD page (Black Box) | Record every drive into the black box (TripRecorder); off ends the trip being recorded. Pinnable |
blackBoxSweepAll | blackBoxSweepAll | bool | true | true/false | OBD page (Black Box) | Also poll every PID the vehicle scan found, one per pass of the live gauges, so the record covers PIDs not on screen. Pinnable |
blackBoxMaxMB | blackBoxMaxMB | int | 500 | 50–5000 (step 50) | OBD page (Black Box) | Cap for the database plus trip files; the oldest closed drives are deleted first. Pinnable |
serviceEnabled | – | bool | false | true/false | Settings > Device > Service (head unit only), or edit the file | Start the service protocol on 127.0.0.1:9876 at every launch, as --service does; read once at startup. save_setting refuses it |
serviceWebRoot | – | string | "" | a directory holding index.html | None (edit the file) | Serve that directory at GET / on the service port instead of the reference client, as --web-root does (the flag wins); read once at startup. save_setting refuses it |
serviceLoopbackOpen | – | bool | true | true/false | None (edit the file) | Programs on this device use the service without pairing. Set false behind a local reverse proxy (it would otherwise make every network client look local). File-only |
serviceTokens | – | list | [] | written by OCTAVE | Settings > Device > Service | Paired devices: {id, name, hash, created, lastUsed, scopes}, the SHA-256 of each token only; a row without scopes (paired before scopes existed) is ["full"]. Session devices of an installed frontend are never stored. File-only: no setter, apply_settings or reset writes it |
obdAutoReconnectAttempts | obdAutoReconnectAttempts | int | 0 | clamped 0 – 10 (0 = disabled) | OBD page (Connection) | Automatic reconnect attempts after a drop |
fuelTankCapacity | fuelTankCapacity | float | 15.0 | – | None | Tank size in gallons. Property and slot exist but nothing reads or writes them today |
get_obd_parameter_enabled(pid) / save_obd_parameter_enabled(pid, on) | obdParameters | object of bool | 123 parameter IDs, 20 enabled (list) | PID name → enabled | Legacy; nothing in the UI writes it any more (enable_all_supported() still can) | Formerly chose which PIDs are polled. Polling is now demand-driven (what is on screen), so this object is kept only for settings-file compatibility. Writes are debounced 800 ms; PIDs missing from the file are merged in from the defaults at load |
homeOBDParameters | homeOBDParameters | QVariantList (strings) | ["SPEED", "RPM", "COOLANT_TEMP", "CONTROL_MODULE_VOLTAGE"] | PID names | OBD page | PIDs shown on the home-screen cards, in order; at most 8, no duplicates (the manager enforces both) |
get_supported_obd_parameters() | supportedOBDParameters | list of strings | [] | PID names | Written by OBDManager after a vehicle scan | PIDs the last scanned vehicle answered; lists the OBD settings page's parameters while offline. The live gate uses obdManager.vehicleSupportedParameters (this session only) instead |
OBD Parameter PIDs
The obdParameters default object holds these 123 keys. The full gauge/PID catalogue with units is in OBD Manager and docs/GAUGE_AUTHORING.md.
Enabled by default (20):
COOLANT_TEMP CONTROL_MODULE_VOLTAGE ENGINE_LOAD THROTTLE_POS INTAKE_TEMP
TIMING_ADVANCE MAF SPEED RPM COMMANDED_EQUIV_RATIO FUEL_LEVEL INTAKE_PRESSURE
SHORT_FUEL_TRIM_1 LONG_FUEL_TRIM_1 O2_B1S1 FUEL_PRESSURE OIL_TEMP
FUEL_ECONOMY DISTANCE_TO_EMPTY IGNITION_TIMING
Disabled by default (103):
RUN_TIME DISTANCE_W_MIL FUEL_RAIL_PRESSURE_VAC FUEL_RAIL_PRESSURE_DIRECT
BAROMETRIC_PRESSURE AMBIANT_AIR_TEMP RELATIVE_THROTTLE_POS THROTTLE_POS_B
ACCELERATOR_POS_D CATALYST_TEMP_B1S1 CATALYST_TEMP_B1S2 EVAP_VAPOR_PRESSURE
SHORT_FUEL_TRIM_2 LONG_FUEL_TRIM_2 O2_B1S2 O2_B2S1 O2_B2S2
DISTANCE_SINCE_DTC_CLEAR WARMUPS_SINCE_DTC_CLEAR ABSOLUTE_LOAD COMMANDED_EGR
EGR_ERROR ETHANOL_PERCENT
O2_B1S3 O2_B1S4 O2_B2S3 O2_B2S4
O2_S1_WR_VOLTAGE … O2_S8_WR_VOLTAGE (8 wide-range O2 voltage sensors)
O2_S1_WR_CURRENT … O2_S8_WR_CURRENT (8 wide-range O2 current sensors)
CATALYST_TEMP_B2S1 CATALYST_TEMP_B2S2
THROTTLE_POS_C ACCELERATOR_POS_E ACCELERATOR_POS_F THROTTLE_ACTUATOR
EVAPORATIVE_PURGE FUEL_RAIL_PRESSURE_ABS FUEL_INJECT_TIMING FUEL_RATE
RUN_TIME_MIL TIME_SINCE_DTC_CLEARED MAX_MAF FUEL_TYPE
EVAP_VAPOR_PRESSURE_ABS EVAP_VAPOR_PRESSURE_ALT
SHORT_O2_TRIM_B1 LONG_O2_TRIM_B1 SHORT_O2_TRIM_B2 LONG_O2_TRIM_B2
RELATIVE_ACCEL_POS HYBRID_BATTERY_REMAINING ELM_VOLTAGE
# multi-value PIDs 0x61+ (obdParameterChanged), all false by default
DEMAND_ENGINE_TORQUE ACTUAL_ENGINE_TORQUE ENGINE_FRICTION_TORQUE
REFERENCE_TORQUE BOOST_PRESSURE_A BOOST_PRESSURE_A_COMMANDED WASTEGATE_A
WASTEGATE_A_COMMANDED MAF_SENSOR_A MAF_SENSOR_B INTAKE_TEMP_B1S1
INTAKE_TEMP_B1S2 INTAKE_TEMP_B2S1 MANIFOLD_SURFACE_TEMP
COOLANT_TEMP_SENSOR_1 COOLANT_TEMP_SENSOR_2 THROTTLE_POS_G
THROTTLE_ACTUATOR_A_COMMANDED RELATIVE_THROTTLE_A EGR_A_COMMANDED
EGR_A_ACTUAL EGR_A_ERROR FUEL_RAIL_PRESSURE_A
FUEL_RAIL_PRESSURE_A_COMMANDED FUEL_RAIL_TEMP_A ENGINE_FUEL_RATE_GS
VEHICLE_FUEL_RATE_GS CYLINDER_FUEL_RATE EXHAUST_FLOW_RATE
TRANSMISSION_GEAR_RATIO RECOMMENDED_GEAR ODOMETER ENGINE_RUN_TIME_TOTAL
ENGINE_IDLE_TIME_TOTAL HYBRID_BATTERY_VOLTAGE HYBRID_BATTERY_CURRENT
EV_BATTERY_HEALTH
ESP32 Volume Knob
All on the Accessories page, Volume Knob sub-section (desktop only). Consumed by ESP32VolumeManager; the LED keys are also read by src/main.cpp to sync the knob's LEDs with the theme.
| Property | JSON Key | Type | Default | Range / Values | Description |
|---|---|---|---|---|---|
esp32VolumeEnabled | esp32VolumeEnabled | bool | true | true/false | Enable the wireless rotary encoder |
esp32VolumePort | esp32VolumePort | QString | "" | Serial port name, or empty | USB serial port of the ESP32-S3 receiver dongle; empty means auto-select the ESP32-S3 at startup |
esp32VolumeStepSize | esp32VolumeStepSize | double | 1.0 | clamped 0.25 – 10, rounded to the nearest 0.25 (UI slider and chips) | Volume change per encoder tick in percent. Stored as a double (float-literal default) so fractional steps (0.25 / 0.5 / 0.75) round-trip |
esp32AutoReconnect | esp32AutoReconnect | bool | true | true/false | Reconnect automatically after the dongle disappears |
esp32LedSleepEnabled | esp32LedSleepEnabled | bool | true | true/false | Let the knob's LEDs turn off (code comment: "LEDs turn off when OCTAVE closes") |
esp32LedColorMode | esp32LedColorMode | QString | "theme" | "theme", "static" (others ignored) | LEDs follow the album-art / theme palette or a fixed colour |
esp32LedStaticColor | esp32LedStaticColor | QString | "#00FFFF" | Hex colour | Colour used when the mode is "static" |
IMU Sensor
Only imuEnabled has a property and slot. The other ten keys have defaults in buildDefaultSettings() (so they are
type-validated) but are read and written through get_setting_with_default() /
save_setting() from the Accessories page (IMU Sensor sub-section) and
frontend/SensorMenu.qml; the BerryIMU manager only reads
imuEnabled.
| Access | JSON Key | Type | Default | Range / Values | Description |
|---|---|---|---|---|---|
imuEnabled property | imuEnabled | bool | true | true/false | Enable the BerryIMU v3 I²C sensor. Pinnable; also on the Accessories widget |
sensorEmitRate / save_sensor_emit_rate() | sensorEmitRate | int | 60 | 10 – 120 Hz (clamped on save and load), UI step 5 | IMU emit rate in Hz; BerryIMUManager applies it at startup and on every change |
sensorDecimalPlaces / save_sensor_decimal_places() | sensorDecimalPlaces | int | 1 | 0 – 4 (clamped) | Decimal places on the Sensors page readouts |
sensorTempUnit / save_sensor_temp_unit() | sensorTempUnit | string | "F" | "F", "C" (other values ignored) | Barometer temperature unit |
sensorAltitudeUnit / save_sensor_altitude_unit() | sensorAltitudeUnit | string | "m" | "m", "ft" (other values ignored) | Barometric altitude unit |
sensorCards.Pitch / save_sensor_card_visible("Pitch", v) | sensorShowPitch | bool | true | true/false | Show the pitch readout on the Sensors page |
sensorCards.Roll / save_sensor_card_visible("Roll", v) | sensorShowRoll | bool | true | true/false | Show the roll readout |
sensorCards.Heading / save_sensor_card_visible("Heading", v) | sensorShowHeading | bool | true | true/false | Show the heading / compass readout |
sensorCards.GForce / save_sensor_card_visible("GForce", v) | sensorShowGForce | bool | true | true/false | Show the G-force readout |
sensorCards.Altitude / save_sensor_card_visible("Altitude", v) | sensorShowAltitude | bool | true | true/false | Show the altitude readout |
sensorCards.Temperature / save_sensor_card_visible("Temperature", v) | sensorShowTemperature | bool | true | true/false | Show the temperature readout |
Gesture Sensor
Accessories page, Gesture Sensor sub-section. Consumed by the GestureManager; the volume actions are executed by src/main.cpp through VolumeController.
| Property | JSON Key | Type | Default | Range / Values | Description |
|---|---|---|---|---|---|
gestureSensorEnabled | gestureSensorEnabled | bool | true | true/false | Enable the PAJ7620U2 I²C gesture sensor. Pinnable; also on the Accessories widget |
get_gesture_mapping() / save_gesture_action() | gestureMapping | object | (below) | Gesture name → action name | Nine gestures mapped to app actions; reset_gesture_mapping() restores the defaults |
gestureVolumeStep | gestureVolumeStep | int | 5 | clamped 1 – 100 (UI slider 1 – 100 or chips 1/2/5/10/15/25/50) | Percent change per volume gesture |
gestureCooldown | gestureCooldown | int | 300 | clamped 100 – 2000 ms (UI slider step 50 or chips 100/200/300/500/1000) | Minimum time between recognised gestures |
Default gesture mapping:
{
"RIGHT": "next_track",
"LEFT": "previous_track",
"UP": "volume_up",
"DOWN": "volume_down",
"FORWARD": "play_pause",
"BACKWARD": "mute_toggle",
"CLOCKWISE": "volume_up",
"COUNTER-CLOCKWISE": "volume_down",
"WAVE": "play_pause"
}
Android Auto, Phone Mirror and AirPlay
Accessories page, Phone Dock sub-section. The scrcpy* keys are pushed into PhoneMirrorManager at startup and the gain, duck and screen-off signals are connected to its setters, so they apply to a running session.
The five androidAuto* keys after androidAutoEnabled belong to built-in Android Auto. They have defaults in buildDefaultSettings() (so they are type-validated) but no property or dedicated slot: the Android Auto card writes them with save_setting() and AndroidAutoManager reads them with get_setting_with_default() at every connection attempt, so a change applies to the next connection (androidAutoBackend at once, through genericSettingChanged → backendChanged).
| Property | JSON Key | Type | Default | Range / Values | Description |
|---|---|---|---|---|---|
androidAutoEnabled | androidAutoEnabled | bool | false | true/false | Show the Android Auto button in the navigation bar ("Show in nav bar" on the Android Auto card) |
generic (get_setting_with_default() / save_setting()) | androidAutoBackend | string | "dhu" | "dhu", "native"; anything else reads as "dhu", and so does every value in a build without the built-in receiver | Android Auto backend: "native" makes OCTAVE the head unit (the Head unit choice "Built-in", shown only when androidAutoManager.nativeAvailable), "dhu" runs Google's Desktop Head Unit ("Google DHU") |
generic (get_setting_with_default(), no UI) | androidAutoCertPath | string | "" | path of a PEM file, or "" | Head-unit certificate for built-in Android Auto. The key is read from the same file when it holds a PRIVATE KEY block, else from the sibling <base name>.key. Empty: headunit.crt + headunit.key in <config dir>/android-auto/. OCTAVE ships no certificate (The head-unit certificate) |
generic (get_setting_with_default() / save_setting()) | androidAutoResolution | string | "1280x720" | "800x480", "1280x720", "1920x1080" (the "Video size" control); any other value falls back to 1280x720 | Size the phone draws built-in Android Auto at, and the touch coordinate space. Takes effect on the next connection |
generic (get_setting_with_default(), no UI) | androidAutoFps | int | 30 | 30 or 60 (60 or more means 60, anything below means 30) | Frame rate asked of the phone for built-in Android Auto |
generic (get_setting_with_default(), no UI) | androidAutoDpi | int | 160 | clamped 80 – 400 | Density announced to the phone for built-in Android Auto |
generic (airPlayManager.setEnabled()) | airPlayEnabled | bool | false | true/false | Advertise OCTAVE as an AirPlay receiver on the local network (iPhone (AirPlay)); "AirPlay receiver" on the iPhone card |
generic (save_setting()) | airPlayName | string | "OCTAVE" | up to 48 characters; empty means "OCTAVE" | What an iPhone lists under Screen Mirroring; the Bluetooth mouse is this plus " Touch" |
generic (save_setting()) | airPlayRequirePin | bool | true | true/false | A new iPhone types the on-screen PIN once (remembered in <config>/airplay/state.json) |
generic (save_setting()) | airPlayResolution | string | "1280x720" | "1280x720", "1920x1080" (the control); any WxH from 640x360 to 3840x2160 | Display size offered to the iPhone; a change restarts the receiver |
generic (airPlayManager.setTouchEnabled()) | airPlayTouchEnabled | bool | true | true/false | OCTAVE is also a Bluetooth LE mouse ("OCTAVE Touch") for the mirrored iPhone (BlueZ builds) |
| generic (calibration, no direct UI) | airPlayTouchScale | double | 600.0 | 100 – 20000 | Mouse counts across the iPhone screen's short side; set by Calibrate touch |
phoneMirrorEnabled | phoneMirrorEnabled | bool | false | true/false | Show the Phone Mirror button in the navigation bar |
scrcpyAudioGain | scrcpyAudioGain | double | 2.0 (+6 dB) | clamped 0.25 – 8.0 (UI slider −12 … +18 dB through save_scrcpy_audio_gain_db() / the scrcpyAudioGainDb property, stored as 10^(dB/20); the default 2.0 is 6.02 dB, shown as 6, and the slider's 6 dB stores 1.9953) | Linear gain applied to phone audio before OCTAVE's volume; the phone's own volume is never changed |
scrcpyAudioDuckEnabled | scrcpyAudioDuckEnabled | bool | true | true/false | Duck local music while the phone is producing sound (navigation prompts, calls). The manager's duckingChanged is routed to MediaManager::setDucking |
scrcpyAudioDuckLevel | scrcpyAudioDuckLevel | double | 0.1 (−20 dB) | clamped 0.0 – 1.0 (UI slider −40 … −6 dB through save_scrcpy_audio_duck_level_db() / scrcpyAudioDuckLevelDb) | Linear factor applied to local music while ducked |
scrcpyPhoneScreenOff | scrcpyPhoneScreenOff | bool | false | true/false | Opt-in: keep the phone's own panel dark while mirroring (off by default because some phones, e.g. Galaxy S22, only dim the panel instead of blanking it); stream and touch keep working and the panel is restored when the session ends. A power-button press wakes the panel for an 8 s grace window (kWakeGraceMs); a second press inside it hands the phone back to the user, and the MirrorKeeper forces a relight after a wake from the lock screen |
scrcpyWirelessAddress | scrcpyWirelessAddress | QString | "" | "ip:port" or "" | Phone last connected over Wi-Fi (QR pairing from Phone Mirror or the Phone Dock card). Written by PhoneMirrorManager through wirelessAddressChanged: when the phone is paired, when Wireless debugging comes back on a new port (found over mDNS), and cleared by Forget. While no phone is usable OCTAVE retries it in the background at most every 5 s |
scrcpyDisplaySize | scrcpyDisplaySize | QString | "auto" | "auto", "WxH" or "" (slot does not validate; the page hands the text to PhoneMirrorManager.setDisplaySize(), which may snap dimensions to multiples of 8, and saves the snapped value back). The "Display Mode" segmented control writes "" (Mirror Phone) or "auto" (Desktop (DeX)); in desktop mode the "Fit to screen" toggle writes "auto" (on) or "1280x800" (off), and the Fit toggle, size field and Text Size slider are hidden in Mirror Phone mode | Virtual display created on the phone (--new-display, Android 11+). "auto" sizes it to the mirror view's pixel size (longest side capped at 1920), so the picture fills the view; empty mirrors the phone's own screen |
scrcpyDisplayDpi | scrcpyDisplayDpi | int | 0 | 0 or 80 – 640 (UI slider Auto, 120 … 400 in steps of 20) | Density of the virtual display ("Text Size"); 0 lets the server scale the phone's own density. Restarts a running session |
scrcpyBitRateUsb | scrcpyBitRateUsb | int (Mbps) | 16 | clamped 2 – 40 (UI slider 4 … 32) | Video bit rate for USB sessions. Restarts a running USB session |
scrcpyBitRateWifi | scrcpyBitRateWifi | int (Mbps) | 8 | clamped 2 – 40 (UI slider 2 … 20) | Video bit rate for Wi-Fi sessions. Restarts a running Wi-Fi session |
scrcpyBitRateAuto | scrcpyBitRateAuto | bool | true | true/false | Adaptive quality: the bit rate follows the link (cut when frames arrive late, raised while they keep up), with scrcpyBitRateUsb / scrcpyBitRateWifi as the ceiling. Restarts a running session |
scrcpyAutoWifi | scrcpyAutoWifi | bool | false | true/false | Before each USB session, switch the phone's adbd to TCP port 5555 (adb tcpip, skipped when already on) and remember its Wi-Fi address in scrcpyWirelessAddress, so it reconnects over Wi-Fi without a cable or the Wireless debugging toggle until the phone reboots. Off by default since 0.9.5: until that reboot the phone's adbd accepts connections on any network it joins. Only the default changed; a saved true is kept |
Settings UI State
Persisted automatically by the settings UI; not shown as controls.
| Access | JSON Key | Type | Default | Range / Values | Description |
|---|---|---|---|---|---|
lastSettingsSection / set_last_settings_section() | lastSettingsSection | QString | "deviceSettings" | the six current sections "displaySettings", "mediaSettings", "obdSettings", "accessoriesSettings", "deviceSettings", "about", plus the legacy IDs "androidAutoSettings", "phoneMirrorSettings", "volumeKnobSettings", "gestureSensorSettings" an older file may carry (others ignored; "clockSettings" migrated to "displaySettings") | Section reopened when the settings menu is entered |
settingsMenuVisibility (JSON string) / is_settings_section_visible() / save_settings_section_visibility() | settingsMenuVisibility | object of bool | displaySettings, mediaSettings, obdSettings, accessoriesSettings, deviceSettings, about: all true | section ID → visible; a section missing from the file (or unknown) reads as visible | Which sections the settings menu shows |
pinnedSettings / toggle_pin_setting() | pinnedSettings | QVariantList (strings) | [] | Registry IDs only | Quick Panel pins, in pin order |
| generic (frontend/settings/ExpandedCards.js) | expandedCards | object of bool | {} | card ID → expanded | Expanded / collapsed state of collapsible settings cards |
QML-Only Keys (no C++ default)
These keys exist only because QML writes them with save_setting(); buildDefaultSettings() does not list them,
so they pass through validation untouched (and unvalidated) via the forward-compatibility copy.
The default shown is the one passed to get_setting_with_default() at the reading call site.
| JSON Key | Type | QML default | Written by | Read by | Description |
|---|---|---|---|---|---|
| rpm_shift_light_enabled | bool | true | OBDMenu RPM popup | ShiftLightManager | Shift light on/off |
| rpm_show_on_home_card | bool | true | OBDMenu RPM popup | ShiftLightManager, OBDMenu | Show the shift light on the home RPM card |
| rpm_max_rpm | int | 8000 | OBDMenu RPM popup | ShiftLightManager, OBDMenu | Redline used by the shift-light stages; the high bound given to migrated legacy {rpm} flags |
| rpm_flags | string (JSON array) | "[]" | shiftLightManager.setFlags() (OBDMenu) | ShiftLightManager | Shift-light stage thresholds and colours |
| rpm_fullscreen_flash_opacity | real | 0.5 | none (read only) | ShiftLightManager | Opacity of the full-screen shift flash (fallback for flags without their own) |
| rpm_shift_light_size | real | 0.5 | none (read only) | ShiftLightManager, OBDMenu | Shift-light size factor |
| rpm_glow_size | real | 0.6 | none (read only) | ShiftLightManager, OBDMenu | Glow radius factor |
| rpm_glow_intensity | real | 0.6 | none (read only) | ShiftLightManager, OBDMenu | Glow intensity |
| rpm_color_transition_speed | int | 100 | none (read only) | ShiftLightManager, OBDMenu | Colour transition speed in ms |
| rpm_pulse_enabled | bool | false | none (read only) | ShiftLightManager, OBDMenu | Pulse the shift light at redline |
| obdCardStyleCircular | bool | false | save_obd_card_style_circular() (OBD page; OBD widget) | settingsManager.obdCardStyleCircular property (OBDMenu, OBD widget) | Circular gauges instead of cards on the OBD grid |
| gaugeTempUnit | string | "C" | save_gauge_temp_unit() (OBD page, "Temperatures in °F"; "C" | "F") | main.cpp → parameterCatalog.setDisplayUnits | Unit of every temperature on the OBD cards, home cards and dashboards (incl. the IMU's BARO_TEMP); the Sensors page keeps sensorTempUnit. Trip History keeps the recorded °C |
| gaugeSpeedUnit | string | "mph" | save_gauge_speed_unit() (OBD page, "Speed in km/h"; "mph" | "km/h") | main.cpp → parameterCatalog.setDisplayUnits | Unit of SPEED on the gauges and dashboards; ALTITUDE there follows sensorAltitudeUnit |
| lastOBDPage | string | "" | OBDHome.pushSubpage() | BottomBar, OBDHome | OBD sub-page the nav button reopens |
| lastSensorPage | string | "" | SensorHome (after a successful push); BottomBar clears it when the page fails to load | BottomBar, SensorHome | Sensor sub-page the nav button reopens |
| activeDashboard | string | "grid" | OBDMenu | OBDMenu | Dashboard ID shown in the OBD view (Gauges & Dashboards) |
| dashboardEditorDraft | string (JSON) | "" | DashboardEditor (saved on exit when there are unsaved changes; _clearStoredDraft() blanks it) | OBDMenu | Unsaved dashboard-editor state |
| dashboardRecentPids | string (JSON array) | "[]" | PIDPicker (each pick, newest first, six at most) | PIDPicker | The dashboard editor's recently picked parameters, listed first under Recent |
| dashboardEditWhileDriving | bool | false | DashboardEditor (Grid · Demo menu, "Allow editing while driving") | DashboardEditor | Keep the dashboard editor usable while the car moves (for a passenger); off, editing pauses above about 5 km/h |
The seven "read only" rpm_* keys have no writer in the current QML; they take effect only if added to the JSON by hand.
In-Memory Only
| Property | Type | Initial value | Description |
|---|---|---|---|
currentVolume / setCurrentVolume() | int | VolumeController::toPercentStatic(startUpVolume) = round(sqrt(startUpVolume) × 100) | Unified 0–100 volume shared by local media, phone mirror and the ESP32 LEDs. VolumeController.applyVolume() updates it during normal volume changes; it is also initialized on load/reset and is never written to the file |
Persistence and File Format
One JSON object, 4-space indented, containing the 96 default keys (nested objects for obdParameters, gestureMapping, customThemes, settingsMenuVisibility, expandedCards) plus any QML-only keys. Writes are:
- Validated once into the in-memory copy (types checked against the defaults; unknown keys kept)
- Coalesced behind a 500 ms single-shot timer, flushed at quit
- Written through
QSaveFileto a temp file in the same directory, fsynced and renamed over the real file in one step byQSaveFile::commit(); on failure the existing file is left untouched chmod 600on Unix
On load, a missing file is created from the defaults, a corrupt file is moved to settingsConfigure.json.corrupt and restored from the last known-good settingsConfigure.json.bak (defaults only if that fails too), and wrong-typed values are replaced individually. Keys of removed features (spotifyClientId, spotifyClientSecret, mediaSource, scrcpyAudioEnabled) are deleted from the file at startup, together with the old Spotify login token. Full detail: Settings Manager: JSON Persistence and Validation.