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.
Pinnable settings
25 of these keys are also registered in the settings registry (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

PropertyJSON KeyTypeDefaultRange / ValuesUIDescription
deviceNamedeviceNameQString"Default Device"–Device page (via Main.updateDeviceName(), which rejects blank names)Display name for this OCTAVE installation
fontSettingfontSettingQString"GoogleSans Regular"–Display pageUI 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
windowStatewindowStateQString"windowed""windowed", "fullscreen", "maximized" (others ignored)Display page (Layout)Window mode applied at startup
screenWidthscreenWidthint1280320 – 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
screenHeightscreenHeightint720300 – 4320 (clamped by the saver)Main.qml on window resize (desktop only)Window height in pixels when windowed

Display

PropertyJSON KeyTypeDefaultRange / ValuesUIDescription
uiScaleuiScalefloat1.0– (UI 0.5 – 2.0, step 0.05)Display pageGlobal UI scale factor (Spacing.globalScale). A stored 0.6 (an old default) is migrated to 1.0 at load. Pinnable
themeSettingthemeSettingQString"CosmicVoyager"Any built-in or custom theme name; "Album Art Capture" enables palette extractionDisplay page; Main.updateTheme()Active colour theme (Theme System). Pinnable (chips from themeList)
customThemescustomThemesobject (property customThemes is a QVariantList of names, customThemeData the whole map){}name → theme object with colour keys and optional albumArt file URLSaved from MediaRoom (save_custom_theme), deleted from the Display pageUser-created themes captured from album art; art files live in theme_art/
colorTransitionMscolorTransitionMsint1000– (UI and registry 0 – 2000, step 50)Display pageDuration of theme colour transitions in ms. Pinnable
songLengthTransitionsongLengthTransitionboolfalsetrue/falseDisplay pageAnimate the theme colour transition over the whole song length instead of colorTransitionMs
showClockshowClockbooltruetrue/falseDisplay page (Clock); Display widgetShow the clock in the bottom bar. Pinnable
clockFormat24HourclockFormat24Hourbooltruetrue/falseDisplay page (Clock)24-hour clock (false = 12-hour). Signal is clockFormatChanged
clockShowSecondsclockShowSecondsboolfalsetrue/falseDisplay page (Clock)Show seconds in the bottom-bar clock
clockSizeclockSizeint18– (UI 10 – 85, step 1)Display page (Clock)Manual clock font size in pixels
clockSizeAutoclockSizeAutobooltrue–Display page (Clock)Fit the clock to its slot in the nav bar (either orientation); when on, clockSize is ignored
bottomBarOrientationbottomBarOrientationQString"bottom""bottom", "side" (UI chips; slot does not validate)Display page (Layout)Navigation bar along the bottom edge or the side
showBottomBarMediaControlsshowBottomBarMediaControlsboolfalsetrue/falseDisplay page (Layout)Play/skip controls inside the navigation bar. Pinnable
settingsLayoutStylesettingsLayoutStyleQString"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
albumArtColorsalbumArtColorsQString (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.

PropertyJSON KeyTypeDefaultRange / ValuesUIDescription
startUpVolumestartUpVolumefloat0.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
mediaFoldermediaFolderQString"" (resolved at load to QStandardPaths::MusicLocation, falling back to ~/Music)Any directory; saving the current value is a no-opMedia page (Library)Root of the local music library; MediaManager connects mediaFolderChanged to set_library_root
autoPlayOnStartupautoPlayOnStartupboolfalsetrue/falseMedia page (Library & Playback); Media widgetResume the last song at launch (not when Android Auto owned the music at shutdown: mediaLastSource). Pinnable
persistShuffleStatepersistShuffleStateboolfalsetrue/falseMedia page (Library & Playback)Restore lastShuffleState at launch. Pinnable
musicButtonDefaultPagemusicButtonDefaultPageQString"mediaRoom""mediaRoom", "mediaPlayer" (others ignored)Media page (Library & Playback)Which view the music nav button opens
returnToLibraryAfterSelectionreturnToLibraryAfterSelectionboolfalsetrue/falseMedia page (Library & Playback)Keep the library open after selecting a track when true; false opens the Now Playing view
showWaveformVisualizershowWaveformVisualizerbooltruetrue/falseMedia page; NowPlayingStudio; Media widgetFFT waveform in MediaRoom (Audio Analyzer). Pinnable
show3DButtonTiltshow3DButtonTiltboolfalsetrue/falseMedia page; NowPlayingStudio3D tilt on prev/play/next press. Pinnable
show3DAlbumPreviewshow3DAlbumPreviewboolfalsetrue/falseMedia page; NowPlayingStudio; Media widgetCoverflow-style side cards around the album art. Pinnable
roundedAlbumArtroundedAlbumArtbooltruetrue/falseMedia page; NowPlayingStudioRounded corners on album art. Pinnable
albumArtCornerRadiusalbumArtCornerRadiusint16clamped 2 – 32Media page; NowPlayingStudioCorner radius in dp when rounded. Pinnable
showAlbumArtShadowshowAlbumArtShadowbooltruetrue/falseMedia page; NowPlayingStudioDrop shadow behind album art. Pinnable
get_setting_with_default() / save_setting()musicDeviceModelstring"Album art""Album art", "Record player", "MP3 player", "CD player", "Cassette player", "iPod" (validated by MusicDevicePreference; SettingsManager checks only the string type)Media page; NowPlayingStudioNow Playing artwork or 3D music-device model. The 3D choices require the Quick3D scene to load; unknown stored choices display as "Album art"
vinylRecordModevinylRecordModeboolfalsetrue/falseMedia page; NowPlayingStudioSpin the album art like a record while playing. Pinnable
audioOutputDeviceaudioOutputDeviceQString""a QAudioDevice id (up to 512 characters), or "" for the system defaultMedia 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
albumArtTransitionalbumArtTransitionQString"Crossfade""Crossfade", "Slide", "Vinyl Lift", "Dissolve", "Flip", "Coverflow", "Conveyor", "Stack", "Depth", "Swing" (others ignored)Media page; NowPlayingStudioTrack-change animation. With 3D preview on, the side cards animate along; off, only the centre card animates. Pinnable
backgroundOverlayOpacitybackgroundOverlayOpacityint80clamped 0 – 100Media page; NowPlayingStudioDark overlay percentage over the blurred background. Pinnable
backgroundBlurRadiusbackgroundBlurRadiusint40– (registry 0 – 100, step 1)Media page; NowPlayingStudioBlur radius of the album-art background. Pinnable
backgroundGridbackgroundGridQString"4x4""Normal", "2x2", "4x4" (UI chips; slot does not validate)Media page; NowPlayingStudioTiling of the album-art background
sideCardOpacitysideCardOpacitydouble0.4clamped 0.1 – 1.0Media page; NowPlayingStudioOpacity of the 3D preview side cards. Pinnable
sideCardAnglesideCardAngleint30clamped 5 – 60Media page; NowPlayingStudioRotation of the side cards in degrees. Pinnable
buttonTiltDurationbuttonTiltDurationint200clamped 50 – 500Media page; NowPlayingStudioButton tilt animation length in ms. Pinnable
textScrollSpeedtextScrollSpeedint5000clamped 1000 – 10000Media page; NowPlayingStudioMarquee scroll duration in ms. Pinnable
directoryHistorydirectoryHistoryQVariantList (strings)[]Most recent first, max 10, no duplicatesNo 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.

PropertyJSON KeyTypeDefaultRange / ValuesDescription
lastShuffleStatelastShuffleStateboolfalsetrue/falseShuffle state at last change; restored only when persistShuffleState is on
lastPlayedSonglastPlayedSongQString""FilenameTrack to resume
lastPlayedPositionlastPlayedPositionint0MillisecondsPosition to resume from
mediaLastSourcemediaLastSourceQString"local"local / phone / androidautoWhich 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
mediaLastPlayingmediaLastPlayingboolfalseWhether that source was playing
lastPlayedPlaylistlastPlayedPlaylistQString""Playlist namePlaylist active at last save

OBD

PropertyJSON KeyTypeDefaultRange / ValuesUIDescription
obdBluetoothPortobdBluetoothPortQString"" (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 AndroidOBD page (Connection); also written by OBDManager when connectingAdapter to connect to; OBDManager reconnects on change (OBD Manager)
obdSavedAdaptersobdSavedAdaptersQString (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 mapsOBD page (Adapters) removes; OBDManager::connect_to_adapter() addsRemembered adapters shown as quick-connect chips
obdFastModeobdFastModebooltruetrue/falseOBD page (Connection); OBD widgetFast polling mode, applied on the next connection. Pinnable
blackBoxEnabledblackBoxEnabledbooltruetrue/falseOBD page (Black Box)Record every drive into the black box (TripRecorder); off ends the trip being recorded. Pinnable
blackBoxSweepAllblackBoxSweepAllbooltruetrue/falseOBD 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
blackBoxMaxMBblackBoxMaxMBint50050–5000 (step 50)OBD page (Black Box)Cap for the database plus trip files; the oldest closed drives are deleted first. Pinnable
serviceEnabled–boolfalsetrue/falseSettings > Device > Service (head unit only), or edit the fileStart 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.htmlNone (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–booltruetrue/falseNone (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 OCTAVESettings > Device > ServicePaired 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
obdAutoReconnectAttemptsobdAutoReconnectAttemptsint0clamped 0 – 10 (0 = disabled)OBD page (Connection)Automatic reconnect attempts after a drop
fuelTankCapacityfuelTankCapacityfloat15.0–NoneTank 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)obdParametersobject of bool123 parameter IDs, 20 enabled (list)PID name → enabledLegacy; 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
homeOBDParametershomeOBDParametersQVariantList (strings)["SPEED", "RPM", "COOLANT_TEMP", "CONTROL_MODULE_VOLTAGE"]PID namesOBD pagePIDs shown on the home-screen cards, in order; at most 8, no duplicates (the manager enforces both)
get_supported_obd_parameters()supportedOBDParameterslist of strings[]PID namesWritten by OBDManager after a vehicle scanPIDs 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.

PropertyJSON KeyTypeDefaultRange / ValuesDescription
esp32VolumeEnabledesp32VolumeEnabledbooltruetrue/falseEnable the wireless rotary encoder
esp32VolumePortesp32VolumePortQString""Serial port name, or emptyUSB serial port of the ESP32-S3 receiver dongle; empty means auto-select the ESP32-S3 at startup
esp32VolumeStepSizeesp32VolumeStepSizedouble1.0clamped 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
esp32AutoReconnectesp32AutoReconnectbooltruetrue/falseReconnect automatically after the dongle disappears
esp32LedSleepEnabledesp32LedSleepEnabledbooltruetrue/falseLet the knob's LEDs turn off (code comment: "LEDs turn off when OCTAVE closes")
esp32LedColorModeesp32LedColorModeQString"theme""theme", "static" (others ignored)LEDs follow the album-art / theme palette or a fixed colour
esp32LedStaticColoresp32LedStaticColorQString"#00FFFF"Hex colourColour 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.

AccessJSON KeyTypeDefaultRange / ValuesDescription
imuEnabled propertyimuEnabledbooltruetrue/falseEnable the BerryIMU v3 I²C sensor. Pinnable; also on the Accessories widget
sensorEmitRate / save_sensor_emit_rate()sensorEmitRateint6010 – 120 Hz (clamped on save and load), UI step 5IMU emit rate in Hz; BerryIMUManager applies it at startup and on every change
sensorDecimalPlaces / save_sensor_decimal_places()sensorDecimalPlacesint10 – 4 (clamped)Decimal places on the Sensors page readouts
sensorTempUnit / save_sensor_temp_unit()sensorTempUnitstring"F""F", "C" (other values ignored)Barometer temperature unit
sensorAltitudeUnit / save_sensor_altitude_unit()sensorAltitudeUnitstring"m""m", "ft" (other values ignored)Barometric altitude unit
sensorCards.Pitch / save_sensor_card_visible("Pitch", v)sensorShowPitchbooltruetrue/falseShow the pitch readout on the Sensors page
sensorCards.Roll / save_sensor_card_visible("Roll", v)sensorShowRollbooltruetrue/falseShow the roll readout
sensorCards.Heading / save_sensor_card_visible("Heading", v)sensorShowHeadingbooltruetrue/falseShow the heading / compass readout
sensorCards.GForce / save_sensor_card_visible("GForce", v)sensorShowGForcebooltruetrue/falseShow the G-force readout
sensorCards.Altitude / save_sensor_card_visible("Altitude", v)sensorShowAltitudebooltruetrue/falseShow the altitude readout
sensorCards.Temperature / save_sensor_card_visible("Temperature", v)sensorShowTemperaturebooltruetrue/falseShow 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.

PropertyJSON KeyTypeDefaultRange / ValuesDescription
gestureSensorEnabledgestureSensorEnabledbooltruetrue/falseEnable the PAJ7620U2 I²C gesture sensor. Pinnable; also on the Accessories widget
get_gesture_mapping() / save_gesture_action()gestureMappingobject(below)Gesture name → action nameNine gestures mapped to app actions; reset_gesture_mapping() restores the defaults
gestureVolumeStepgestureVolumeStepint5clamped 1 – 100 (UI slider 1 – 100 or chips 1/2/5/10/15/25/50)Percent change per volume gesture
gestureCooldowngestureCooldownint300clamped 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).

PropertyJSON KeyTypeDefaultRange / ValuesDescription
androidAutoEnabledandroidAutoEnabledboolfalsetrue/falseShow the Android Auto button in the navigation bar ("Show in nav bar" on the Android Auto card)
generic (get_setting_with_default() / save_setting())androidAutoBackendstring"dhu""dhu", "native"; anything else reads as "dhu", and so does every value in a build without the built-in receiverAndroid 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)androidAutoCertPathstring""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())androidAutoResolutionstring"1280x720""800x480", "1280x720", "1920x1080" (the "Video size" control); any other value falls back to 1280x720Size 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)androidAutoFpsint3030 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)androidAutoDpiint160clamped 80 – 400Density announced to the phone for built-in Android Auto
generic (airPlayManager.setEnabled())airPlayEnabledboolfalsetrue/falseAdvertise OCTAVE as an AirPlay receiver on the local network (iPhone (AirPlay)); "AirPlay receiver" on the iPhone card
generic (save_setting())airPlayNamestring"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())airPlayRequirePinbooltruetrue/falseA new iPhone types the on-screen PIN once (remembered in <config>/airplay/state.json)
generic (save_setting())airPlayResolutionstring"1280x720""1280x720", "1920x1080" (the control); any WxH from 640x360 to 3840x2160Display size offered to the iPhone; a change restarts the receiver
generic (airPlayManager.setTouchEnabled())airPlayTouchEnabledbooltruetrue/falseOCTAVE is also a Bluetooth LE mouse ("OCTAVE Touch") for the mirrored iPhone (BlueZ builds)
generic (calibration, no direct UI)airPlayTouchScaledouble600.0100 – 20000Mouse counts across the iPhone screen's short side; set by Calibrate touch
phoneMirrorEnabledphoneMirrorEnabledboolfalsetrue/falseShow the Phone Mirror button in the navigation bar
scrcpyAudioGainscrcpyAudioGaindouble2.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
scrcpyAudioDuckEnabledscrcpyAudioDuckEnabledbooltruetrue/falseDuck local music while the phone is producing sound (navigation prompts, calls). The manager's duckingChanged is routed to MediaManager::setDucking
scrcpyAudioDuckLevelscrcpyAudioDuckLeveldouble0.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
scrcpyPhoneScreenOffscrcpyPhoneScreenOffboolfalsetrue/falseOpt-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
scrcpyWirelessAddressscrcpyWirelessAddressQString"""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
scrcpyDisplaySizescrcpyDisplaySizeQString"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 modeVirtual 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
scrcpyDisplayDpiscrcpyDisplayDpiint00 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
scrcpyBitRateUsbscrcpyBitRateUsbint (Mbps)16clamped 2 – 40 (UI slider 4 … 32)Video bit rate for USB sessions. Restarts a running USB session
scrcpyBitRateWifiscrcpyBitRateWifiint (Mbps)8clamped 2 – 40 (UI slider 2 … 20)Video bit rate for Wi-Fi sessions. Restarts a running Wi-Fi session
scrcpyBitRateAutoscrcpyBitRateAutobooltruetrue/falseAdaptive 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
scrcpyAutoWifiscrcpyAutoWifiboolfalsetrue/falseBefore 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.

AccessJSON KeyTypeDefaultRange / ValuesDescription
lastSettingsSection / set_last_settings_section()lastSettingsSectionQString"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()settingsMenuVisibilityobject of booldisplaySettings, mediaSettings, obdSettings, accessoriesSettings, deviceSettings, about: all truesection ID → visible; a section missing from the file (or unknown) reads as visibleWhich sections the settings menu shows
pinnedSettings / toggle_pin_setting()pinnedSettingsQVariantList (strings)[]Registry IDs onlyQuick Panel pins, in pin order
generic (frontend/settings/ExpandedCards.js)expandedCardsobject of bool{}card ID → expandedExpanded / 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 KeyTypeQML defaultWritten byRead byDescription
rpm_shift_light_enabledbooltrueOBDMenu RPM popupShiftLightManagerShift light on/off
rpm_show_on_home_cardbooltrueOBDMenu RPM popupShiftLightManager, OBDMenuShow the shift light on the home RPM card
rpm_max_rpmint8000OBDMenu RPM popupShiftLightManager, OBDMenuRedline used by the shift-light stages; the high bound given to migrated legacy {rpm} flags
rpm_flagsstring (JSON array)"[]"shiftLightManager.setFlags() (OBDMenu)ShiftLightManagerShift-light stage thresholds and colours
rpm_fullscreen_flash_opacityreal0.5none (read only)ShiftLightManagerOpacity of the full-screen shift flash (fallback for flags without their own)
rpm_shift_light_sizereal0.5none (read only)ShiftLightManager, OBDMenuShift-light size factor
rpm_glow_sizereal0.6none (read only)ShiftLightManager, OBDMenuGlow radius factor
rpm_glow_intensityreal0.6none (read only)ShiftLightManager, OBDMenuGlow intensity
rpm_color_transition_speedint100none (read only)ShiftLightManager, OBDMenuColour transition speed in ms
rpm_pulse_enabledboolfalsenone (read only)ShiftLightManager, OBDMenuPulse the shift light at redline
obdCardStyleCircularboolfalsesave_obd_card_style_circular() (OBD page; OBD widget)settingsManager.obdCardStyleCircular property (OBDMenu, OBD widget)Circular gauges instead of cards on the OBD grid
gaugeTempUnitstring"C"save_gauge_temp_unit() (OBD page, "Temperatures in °F"; "C" | "F")main.cpp → parameterCatalog.setDisplayUnitsUnit 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
gaugeSpeedUnitstring"mph"save_gauge_speed_unit() (OBD page, "Speed in km/h"; "mph" | "km/h")main.cpp → parameterCatalog.setDisplayUnitsUnit of SPEED on the gauges and dashboards; ALTITUDE there follows sensorAltitudeUnit
lastOBDPagestring""OBDHome.pushSubpage()BottomBar, OBDHomeOBD sub-page the nav button reopens
lastSensorPagestring""SensorHome (after a successful push); BottomBar clears it when the page fails to loadBottomBar, SensorHomeSensor sub-page the nav button reopens
activeDashboardstring"grid"OBDMenuOBDMenuDashboard ID shown in the OBD view (Gauges & Dashboards)
dashboardEditorDraftstring (JSON)""DashboardEditor (saved on exit when there are unsaved changes; _clearStoredDraft() blanks it)OBDMenuUnsaved dashboard-editor state
dashboardRecentPidsstring (JSON array)"[]"PIDPicker (each pick, newest first, six at most)PIDPickerThe dashboard editor's recently picked parameters, listed first under Recent
dashboardEditWhileDrivingboolfalseDashboardEditor (Grid · Demo menu, "Allow editing while driving")DashboardEditorKeep 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

PropertyTypeInitial valueDescription
currentVolume / setCurrentVolume()intVolumeController::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:

  1. Validated once into the in-memory copy (types checked against the defaults; unknown keys kept)
  2. Coalesced behind a 500 ms single-shot timer, flushed at quit
  3. Written through QSaveFile to a temp file in the same directory, fsynced and renamed over the real file in one step by QSaveFile::commit(); on failure the existing file is left untouched
  4. chmod 600 on 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.