Signals & Slots Reference
Public API of the C++ objects registered as a QML context property, verified against the headers in src/managers/, src/util/ and src/dev/.
tst_apidocs). When the two disagree, the generated one is right.
QObject member of AppCore (src/appcore.{h,cpp}), set as a context property by src/main.cpp, and QML reaches it by that name: mediaManager.next_track(), settingsManager.themeSetting, obdManager.onRpmChanged. Other context values are screenAutoScale (float, short screen edge / 720 clamped to 0.4–3.0), isAndroid (bool, true only in the Android build), wikiViewerAvailable (bool: an embedded web viewer is available), and wikiHomeUrl (QUrl: the wiki entry point). The qmlRegisterType'd OCTAVE.AndroidAuto.EmbeddedDhuItem and the QML singletons (OBDParameterModel, WidgetCatalog, …) are documented in Components, not here.
Context property registry
17 QObject context properties, plus the dhuCapture sub-object and the developer-only perfMonitor. The scalar context values are described above.
| QML name | Class & file | Notes |
|---|---|---|
settingsManager | SettingsManager — src/managers/settingsmanager.h | |
assetStore | AssetStore — src/util/assetstore.h | Local URLs for bundled assets, with APK extraction on Android |
clock | Clock — src/managers/clock.h | |
diagnosticsManager | DiagnosticsManager — src/managers/diagnosticsmanager.h | |
networkManager | NetworkManager — src/managers/networkmanager.h | |
volumeController | VolumeController — src/managers/volumecontroller.h | |
mediaManager | MediaManager — src/managers/mediamanager.h | |
audioAnalyzer | AudioAnalyzer — src/managers/audioanalyzer.h | |
obdManager | OBDManager — src/managers/obdmanager.h | |
obdAdapterFinder | OBDAdapterFinder — src/managers/obdadapterfinder.h | supported is true on desktop Linux only (phase 1) |
parameterCatalog | ParameterCatalog — src/managers/parametercatalog.h | Title / unit / range / kind of every OBD PID and IMU reading |
nowPlaying | NowPlaying — src/managers/nowplaying.h | What is playing and its controls, phone or local |
paramStore | ParameterStore — src/managers/parameterstore.h | Latest value of every OBD PID and IMU reading, one notifying key per id |
shiftLightManager | ShiftLightManager — src/managers/shiftlightmanager.h | RPM shift light: active flag and flash phase |
tripRecorder | TripRecorder — src/managers/triprecorder.h | Black box: every drive into SQLite + CSV / JSON files |
esp32VolumeManager | ESP32VolumeManager — src/managers/esp32volumemanager.h | Mobile builds register a stub (see the section) |
berryIMU | BerryIMUManager — src/managers/berryimumanager.h | Android build uses a QtSensors implementation |
gestureSensor | GestureManager — src/managers/gesturemanager.h | Mobile builds register a stub (see the section) |
androidAutoManager | AndroidAutoManager — src/managers/androidautomanager.h | Mobile builds register a stub (see the section) |
dhuCapture | DhuCapture — src/managers/androidautomanager.h | Not registered directly; reached via androidAutoManager.dhuCapture |
airPlayManager | AirPlayManager — src/managers/airplaymanager.h | available false without the AirPlay receiver (Linux only so far) |
phoneMirrorManager | PhoneMirrorManager — src/managers/phonemirrormanager.h | Mobile builds register a stub (see the section) |
dashboardManager | DashboardManager — src/managers/dashboardmanager.h | |
frontendManager | FrontendManager — src/managers/frontendmanager.h | |
perfMonitor | PerfMonitor — src/dev/perfmonitor.h | Only with --profile, in OCTAVE_DEV_TOOLS builds; registered by OctaveDev::Runtime |
Reading this page
- Properties are
Q_PROPERTYdeclarations. Almost all are read-only from QML; call the matchingsave_*/set*slot to change them. Notify signal is what a QML binding oron<Name>Changedhandler listens to. - Invokables & Slots are
Q_INVOKABLEmethods andpublic slots. Signatures show the C++ parameter types withoutconst/&.QVariantList/QStringListarrive in QML as JS arrays,QVariantMapas objects. - Signals list the payload the QML handler receives.
- Android marks members that only do something on Android.
- Where the class has a separate mobile (
Q_OS_MOBILE) stub, the info box at the top of the section lists what the stub lacks.
SettingsManager — settingsManager
src/managers/settingsmanager.h — QML context property settingsManager
Central settings store. Persists settingsConfigure.json in the OS config dir (writes are debounced; flushPendingSave() forces them). Settings with a dedicated property follow a triple: a read-only Q_PROPERTY, a <name>Changed signal and a save_<snake_name>() slot. Types and defaults for every key are in the Settings Reference.
Properties
| Property | Type | Access | Notify signal | Description |
|---|---|---|---|---|
deviceName | QString | read-only | deviceNameChanged | Name shown on the home screen / About page — Settings Reference |
fontSetting | QString | read-only | fontSettingChanged | UI font family (default "GoogleSans Regular"; a stored legacy "Google Sans" is migrated at load) — Settings Reference |
themeSetting | QString | read-only | themeSettingChanged | Active theme name (built-in or custom) — Settings Reference |
uiScale | float | read-only | uiScaleChanged | Global UI scale factor multiplied with screenAutoScale — Settings Reference |
colorTransitionMs | int | read-only | colorTransitionMsChanged | Duration of theme colour transitions in ms (registry slider 0–2000, step 50) — Settings Reference |
songLengthTransition | bool | read-only | songLengthTransitionChanged | Spread the album-art colour transition over the length of the song — Settings Reference |
showClock | bool | read-only | showClockChanged | Show the clock in the bottom bar — Settings Reference |
clockFormat24Hour | bool | read-only | clockFormatChanged | 24-hour clock (notify signal is clockFormatChanged) — Settings Reference |
clockShowSeconds | bool | read-only | clockShowSecondsChanged | Show seconds in the clock (Clock re-evaluates every second) — Settings Reference |
clockSize | int | read-only | clockSizeChanged | Clock font size in px — Settings Reference |
clockSizeAuto | bool | read-only | clockSizeAutoChanged | Fit the clock to its nav bar slot instead of using clockSize |
backgroundGrid | QString | read-only | backgroundGridChanged | Album-art background tiling: "Normal", "2x2", "4x4" — Settings Reference |
screenWidth | int | read-only | screenWidthChanged | Window width (windowed mode) — Settings Reference |
screenHeight | int | read-only | screenHeightChanged | Window height (windowed mode) — Settings Reference |
backgroundBlurRadius | int | read-only | backgroundBlurRadiusChanged | Blur radius applied to the album-art background — Settings Reference |
bottomBarOrientation | QString | read-only | bottomBarOrientationChanged | Navigation bar placement: "bottom" / "side" — Settings Reference |
showBottomBarMediaControls | bool | read-only | showBottomBarMediaControlsChanged | Show play/skip controls in the bottom bar — Settings Reference |
settingsLayoutStyle | QString | read-only | settingsLayoutStyleChanged | Which Settings*Layout.qml renders the menu: "Carousel", "Sidebar", "Hub", "Dashboard" — Settings Reference |
albumArtColors | QVariantMap | read-only | albumArtColorsChanged | The last extracted album-art palette as a theme object, empty when unset (stored as a JSON string; written by the media managers via set_album_art_colors) — Settings Reference |
settingsMenuVisibility | QString | read-only | settingsMenuVisibilityChanged | JSON map of settings section id → visible (displaySettings, mediaSettings, obdSettings, accessoriesSettings, deviceSettings, about; a section missing from the file counts as visible) — Settings Reference |
startUpVolume | float | read-only | startUpVolumeChanged | Linear gain applied at launch (0–1; default 0.1) — Settings Reference |
currentVolume | int | read-only | currentVolumeChanged | Live volume percent (0–100); set only through volumeController.applyVolume() — Settings Reference |
mediaFolder | QString | read-only | mediaFolderChanged | Root folder of the local music library — Settings Reference |
autoPlayOnStartup | bool | read-only | autoPlayOnStartupChanged | Resume playback automatically at launch — Settings Reference |
persistShuffleState | bool | read-only | persistShuffleStateChanged | Remember shuffle across restarts — Settings Reference |
lastShuffleState | bool | read-only | lastShuffleStateChanged | Shuffle state saved whenever it changes — Settings Reference |
lastPlayedSong | QString | read-only | lastPlayedSongChanged | Filename to resume — Settings Reference |
lastPlayedPosition | int | read-only | lastPlayedPositionChanged | Resume position in ms — Settings Reference |
lastPlayedPlaylist | QString | read-only | lastPlayedPlaylistChanged | Playlist to resume — Settings Reference |
musicButtonDefaultPage | QString | read-only | musicButtonDefaultPageChanged | View the music nav button opens: "mediaRoom" / "mediaPlayer" — Settings Reference |
returnToLibraryAfterSelection | bool | read-only | returnToLibraryAfterSelectionChanged | Keep the library open after selecting a track; false opens Now Playing — Settings Reference |
showWaveformVisualizer | bool | read-only | showWaveformVisualizerChanged | Show the FFT waveform on the player — Settings Reference |
show3DButtonTilt | bool | read-only | show3DButtonTiltChanged | 3D tilt effect on buttons — Settings Reference |
show3DAlbumPreview | bool | read-only | show3DAlbumPreviewChanged | Show previous/next album art side cards — Settings Reference |
roundedAlbumArt | bool | read-only | roundedAlbumArtChanged | Round the album-art corners — Settings Reference |
albumArtCornerRadius | int | read-only | albumArtCornerRadiusChanged | Corner radius in px when roundedAlbumArt is on — Settings Reference |
showAlbumArtShadow | bool | read-only | showAlbumArtShadowChanged | Drop shadow under album art — Settings Reference |
vinylRecordMode | bool | read-only | vinylRecordModeChanged | Spin the album art like a record — Settings Reference |
audioOutputDevice | QString | read-only | audioOutputDeviceChanged | The audio output picked in Settings > Media (a QAudioDevice id); empty = the system default — Settings Reference |
albumArtTransition | QString | read-only | albumArtTransitionChanged | Track-change animation ("Crossfade", "Slide", "Vinyl Lift", "Flip", "Coverflow", …) — Settings Reference |
backgroundOverlayOpacity | int | read-only | backgroundOverlayOpacityChanged | Opacity (0–100) of the dark overlay on the background — Settings Reference |
sideCardOpacity | double | read-only | sideCardOpacityChanged | Opacity of the 3D side cards — Settings Reference |
sideCardAngle | int | read-only | sideCardAngleChanged | Rotation angle of the 3D side cards — Settings Reference |
buttonTiltDuration | int | read-only | buttonTiltDurationChanged | Button tilt animation duration in ms — Settings Reference |
textScrollSpeed | int | read-only | textScrollSpeedChanged | Marquee scroll speed for long titles — Settings Reference |
obdBluetoothPort | QString | read-only | obdBluetoothPortChanged | OBD adapter identifier: serial port / COM port / /dev path, or BLE MAC on Android — Settings Reference |
obdSavedAdapters | QVariantList | read-only | obdSavedAdaptersChanged | Remembered {name, mac} adapters (stored as a JSON string), most recent first — Settings Reference |
obdFastMode | bool | read-only | obdFastModeChanged | Adapter fast-mode option, applied at the next connection; on-screen demand independently selects the PIDs — Settings Reference |
obdCardStyleCircular | bool | read-only | obdCardStyleCircularChanged | Circular gauges instead of square cards on the OBD parameter grid (default false) |
gaugeTempUnit, gaugeSpeedUnit | QString | read-only | gaugeTempUnitChanged, gaugeSpeedUnitChanged | Units the gauges and dashboards show: "C" | "F" (default C), "mph" | "km/h" (default mph); ParameterCatalog converts |
obdAutoReconnectAttempts | int | read-only | obdAutoReconnectAttemptsChanged | Max automatic reconnect attempts before giving up — Settings Reference |
fuelTankCapacity | float | read-only | fuelTankCapacityChanged | Legacy tank-capacity setting in gallons; no current consumer uses it — Settings Reference |
homeOBDParameters | QVariantList | read-only | homeOBDParametersChanged | Ordered list of PID ids shown on the home screen — Settings Reference |
maxHomeObdParameters | int | constant | — | Most home-screen OBD cards (8) |
customThemes | QVariantList | read-only | customThemesChanged | Names of user-saved themes — Settings Reference |
customThemeData | QVariantMap | read-only | customThemesChanged | Every saved custom theme, name → theme object, as on disk; Main.qml assigns it to Style.customThemes |
directoryHistory | QVariantList | read-only | directoryHistoryChanged | Recently used media folders (max 10) — Settings Reference |
windowState | QString | read-only | windowStateChanged | "windowed" / "fullscreen" / "maximized" — Settings Reference |
lastSettingsSection | QString | read-only | lastSettingsSectionChanged | Settings section to reopen (e.g. "mediaSettings") — Settings Reference |
androidAutoEnabled | bool | read-only | androidAutoEnabledChanged | Show the Android Auto page — Settings Reference |
phoneMirrorEnabled | bool | read-only | phoneMirrorEnabledChanged | Show the Phone Mirror page — Settings Reference |
scrcpyDisplaySize | QString | read-only | scrcpyDisplaySizeChanged | Virtual display "WxH" or "auto" for mirroring; "" mirrors the phone screen — Settings Reference |
scrcpyAudioGain | double | read-only | scrcpyAudioGainChanged | Linear gain applied to phone audio before OCTAVE volume — Settings Reference |
scrcpyAudioDuckEnabled | bool | read-only | scrcpyAudioDuckEnabledChanged | Duck local media while the phone produces sound — Settings Reference |
scrcpyAudioDuckLevel | double | read-only | scrcpyAudioDuckLevelChanged | Duck factor (linear, e.g. 0.1 = -20 dB) — Settings Reference |
scrcpyAudioGainDb, scrcpyAudioDuckLevelDb | double | read-only | scrcpyAudioGainChanged, scrcpyAudioDuckLevelChanged | The same two levels in dB (20 log10; silence −120), as the Phone Audio sliders show them |
scrcpyPhoneScreenOff | bool | read-only | scrcpyPhoneScreenOffChanged | Keep the handset panel dark while mirroring — Settings Reference |
scrcpyDisplayDpi | int | read-only | scrcpyDisplayDpiChanged | Virtual display density, 0 = auto — Settings Reference |
scrcpyBitRateUsb, scrcpyBitRateWifi | int | read-only | scrcpyBitRateUsbChanged, scrcpyBitRateWifiChanged | Mirror video bit rate per link, Mbps — Settings Reference |
scrcpyAutoWifi | bool | read-only | scrcpyAutoWifiChanged | Enable adb over Wi-Fi on a USB phone — Settings Reference |
scrcpyBitRateAuto | bool | read-only | scrcpyBitRateAutoChanged | Adaptive mirror bit rate — Settings Reference |
scrcpyWirelessAddress | QString | read-only | scrcpyWirelessAddressChanged | Phone last connected over Wi-Fi — Settings Reference |
esp32VolumeEnabled | bool | read-only | esp32VolumeEnabledChanged | Enable the ESP32 rotary knob — Settings Reference |
esp32VolumePort | QString | read-only | esp32VolumePortChanged | Serial port of the ESP32 (default "" = auto-select the ESP32-S3 at startup) — Settings Reference |
esp32VolumeStepSize | double | read-only | esp32VolumeStepSizeChanged | Volume percent per encoder tick (float, default 1.0; fractional steps such as 0.25 persist) — Settings Reference |
esp32AutoReconnect | bool | read-only | esp32AutoReconnectChanged | Reconnect automatically when the knob is unplugged/replugged — Settings Reference |
esp32LedSleepEnabled | bool | read-only | esp32LedSleepEnabledChanged | Let the knob LED sleep when idle — Settings Reference |
esp32LedColorMode | QString | read-only | esp32LedColorModeChanged | "theme" / "static" LED colour source — Settings Reference |
esp32LedStaticColor | QString | read-only | esp32LedStaticColorChanged | Hex colour used when esp32LedColorMode is static — Settings Reference |
imuEnabled | bool | read-only | imuEnabledChanged | Enable the BerryIMU sensor — Settings Reference |
sensorEmitRate | int | read-only | sensorEmitRateChanged | IMU emit rate in Hz (10–120, default 60); BerryIMUManager follows it — Settings Reference |
sensorDecimalPlaces | int | read-only | sensorDecimalPlacesChanged | Sensors page precision, 0–4 (default 1) |
sensorTempUnit | QString | read-only | sensorTempUnitChanged | "F" (default) or "C" |
sensorAltitudeUnit | QString | read-only | sensorAltitudeUnitChanged | "m" (default) or "ft" |
sensorCards | QVariantMap | read-only | sensorCardsChanged | Which Sensors page readouts show: Pitch, Roll, Heading, GForce, Altitude, Temperature → bool (saved as sensorShow<card>) |
gestureSensorEnabled | bool | read-only | gestureSensorEnabledChanged | Enable the PAJ7620U2 gesture sensor — Settings Reference |
gestureVolumeStep | int | read-only | gestureVolumeStepChanged | Volume percent per volume gesture — Settings Reference |
gestureCooldown | int | read-only | gestureCooldownChanged | Minimum ms between accepted gestures — Settings Reference |
pinnedSettings | QVariantList | read-only | pinnedSettingsChanged | Setting ids pinned to the Quick Panel — Settings Reference |
Invokables & Slots
| Signature | Returns | Description |
|---|---|---|
save_device_name(QString name) | void | Save deviceName, then emit deviceNameChanged |
save_font_setting(QString font) | void | Save fontSetting, then emit fontSettingChanged |
save_theme_setting(QString theme) | void | Save themeSetting, then emit themeSettingChanged |
save_ui_scale(float scale) | void | Save uiScale, then emit uiScaleChanged |
save_color_transition_ms(int ms) | void | Save colorTransitionMs, then emit colorTransitionMsChanged |
save_song_length_transition(bool enabled) | void | Save songLengthTransition, then emit songLengthTransitionChanged |
save_show_clock(bool show) | void | Save showClock, then emit showClockChanged |
save_clock_format(bool is24hour) | void | Save clockFormat24Hour, then emit clockFormatChanged |
save_clock_show_seconds(bool show) | void | Save clockShowSeconds, then emit clockShowSecondsChanged |
save_clock_size(int size) | void | Save clockSize, then emit clockSizeChanged |
save_clock_size_auto(bool autoSize) | void | Persist clockSizeAuto, then emit clockSizeAutoChanged |
save_background_grid(QString grid) | void | Save backgroundGrid, then emit backgroundGridChanged |
save_screen_width(int width) | void | Save screenWidth clamped to 400–7680 (kMin/kMaxScreenWidth), then emit screenWidthChanged; an unchanged value does nothing |
save_screen_height(int height) | void | Save screenHeight clamped to 300–4320, then emit screenHeightChanged; an unchanged value does nothing |
save_background_blur_radius(int radius) | void | Save backgroundBlurRadius, then emit backgroundBlurRadiusChanged |
save_bottom_bar_orientation(QString orientation) | void | Save bottomBarOrientation, then emit bottomBarOrientationChanged |
save_show_bottom_bar_media_controls(bool show) | void | Save showBottomBarMediaControls, then emit showBottomBarMediaControlsChanged |
save_settings_layout_style(QString style) | void | Save settingsLayoutStyle, then emit settingsLayoutStyleChanged |
set_album_art_colors(QString colorsJson) | void | Cache the latest extracted palette JSON so the theme survives a restart |
save_start_volume(float volume) | void | Save startUpVolume, then emit startUpVolumeChanged |
setCurrentVolume(int volume) | void | Store the live volume percent and emit currentVolumeChanged (not written to disk). Call volumeController.applyVolume() instead of this from QML |
save_media_folder(QString folderPath) | void | Save mediaFolder, then emit mediaFolderChanged |
save_media_folder_url(QUrl url) | bool | Save a picked folder: file:// or an Android external-storage content:// tree mapped to its /storage path (src/util/pathurl.h); false, nothing saved, when it can't be a path |
save_auto_play_on_startup(bool enabled) | void | Save autoPlayOnStartup, then emit autoPlayOnStartupChanged |
save_persist_shuffle_state(bool enabled) | void | Save persistShuffleState, then emit persistShuffleStateChanged |
save_last_shuffle_state(bool enabled) | void | Save lastShuffleState, then emit lastShuffleStateChanged |
save_last_played_song(QString filename) | void | Save lastPlayedSong, then emit lastPlayedSongChanged |
save_last_played_position(int positionMs) | void | Save lastPlayedPosition, then emit lastPlayedPositionChanged |
save_last_played_playlist(QString playlistName) | void | Save lastPlayedPlaylist, then emit lastPlayedPlaylistChanged |
save_playback_state(QString song, int positionMs, QString playlist) | void | Write song, position (ms) and playlist in one disk write for resume-on-launch |
save_music_button_default_page(QString page) | void | Save musicButtonDefaultPage, then emit musicButtonDefaultPageChanged |
save_return_to_library_after_selection(bool returnToLibrary) | void | Save returnToLibraryAfterSelection, then emit returnToLibraryAfterSelectionChanged |
save_show_waveform_visualizer(bool enabled) | void | Save showWaveformVisualizer, then emit showWaveformVisualizerChanged |
save_show_3d_button_tilt(bool enabled) | void | Save show3DButtonTilt, then emit show3DButtonTiltChanged |
save_show_3d_album_preview(bool enabled) | void | Save show3DAlbumPreview, then emit show3DAlbumPreviewChanged |
save_rounded_album_art(bool enabled) | void | Save roundedAlbumArt, then emit roundedAlbumArtChanged |
save_album_art_corner_radius(int radius) | void | Save albumArtCornerRadius, then emit albumArtCornerRadiusChanged |
save_show_album_art_shadow(bool enabled) | void | Save showAlbumArtShadow, then emit showAlbumArtShadowChanged |
save_vinyl_record_mode(bool enabled) | void | Save vinylRecordMode, then emit vinylRecordModeChanged |
save_audio_output_device(QString id) | void | Save audioOutputDevice (any id up to 512 characters, kept while unplugged; empty = system default), then emit audioOutputDeviceChanged |
save_album_art_transition(QString transition) | void | Save albumArtTransition, then emit albumArtTransitionChanged |
save_background_overlay_opacity(int opacity) | void | Save backgroundOverlayOpacity, then emit backgroundOverlayOpacityChanged |
save_side_card_opacity(double opacity) | void | Save sideCardOpacity, then emit sideCardOpacityChanged |
save_side_card_angle(int angle) | void | Save sideCardAngle, then emit sideCardAngleChanged |
save_button_tilt_duration(int duration) | void | Save buttonTiltDuration, then emit buttonTiltDurationChanged |
save_text_scroll_speed(int speed) | void | Save textScrollSpeed, then emit textScrollSpeedChanged |
save_obd_bluetooth_port(QString port) | void | Save obdBluetoothPort, then emit obdBluetoothPortChanged |
add_obd_saved_adapter(QString name, QString mac) | void | Upsert a {name, mac} entry (MAC upper-cased) at the front of obdSavedAdapters |
remove_obd_saved_adapter(QString mac) | void | Drop the entry with this MAC from obdSavedAdapters |
save_obd_fast_mode(bool enabled) | void | Save obdFastMode, then emit obdFastModeChanged |
save_obd_card_style_circular(bool circular) | void | Save obdCardStyleCircular, then emit obdCardStyleCircularChanged |
save_gauge_temp_unit(QString unit), save_gauge_speed_unit(QString unit) | void | Save a gauge unit (other values ignored), then emit its notify |
save_obd_auto_reconnect_attempts(int attempts) | void | Save obdAutoReconnectAttempts, then emit obdAutoReconnectAttemptsChanged |
save_fuel_tank_capacity(float capacity) | void | Save fuelTankCapacity, then emit fuelTankCapacityChanged |
save_obd_parameter_enabled(QString parameter, bool enabled) | void | Legacy per-PID enable flag, kept for settings-file compatibility; writes are debounced (800 ms) then obdParametersChanged fires. No longer drives polling (see obdManager.setParameterDemand) |
save_supported_obd_parameters(QVariantList parameters) | void | Store the PID list found by the last vehicle scan; emits supportedOBDParametersChanged |
save_home_obd_parameters(QVariantList parameters) | void | Store the ordered home-screen PID list (deduplicated, capped at 8); emits homeOBDParametersChanged |
add_home_obd_parameter(QString id, int index = -1) | bool | Insert at index (-1 = end); false if already there or the grid is full |
remove_home_obd_parameter(QString id) | bool | Remove a card; false if it isn't there |
move_home_obd_parameter(int from, int to) | bool | Move a card; the ones in between shift (past the end = last) |
save_to_directory_history(QString folderPath) | void | Prepend a folder to directoryHistory (deduplicated, capped at 10) |
remove_from_directory_history(QString folderPath) | void | Remove a folder from directoryHistory |
save_custom_theme(QString name, QVariantMap theme, QString albumArtSource) | void | Store a theme object under name; optional albumArtSource records the art the palette came from. Emits customThemesChanged |
delete_custom_theme(QString name) | void | Delete a custom theme by name |
save_window_state(QString state) | void | Persist the window mode; ignores values other than windowed / fullscreen / maximized |
set_last_settings_section(QString section) | void | Persist which settings section was open; accepts the six current section ids (displaySettings, mediaSettings, obdSettings, accessoriesSettings, deviceSettings, about) plus the legacy androidAutoSettings / phoneMirrorSettings / volumeKnobSettings / gestureSensorSettings; anything else is ignored |
save_settings_section_visibility(QString section, bool visible) | void | Show/hide one settings section in the hub; emits settingsMenuVisibilityChanged |
save_android_auto_enabled(bool enabled) | void | Save androidAutoEnabled, then emit androidAutoEnabledChanged |
save_phone_mirror_enabled(bool enabled) | void | Save phoneMirrorEnabled, then emit phoneMirrorEnabledChanged |
save_scrcpy_display_size(QString size) | void | Save scrcpyDisplaySize, then emit scrcpyDisplaySizeChanged |
save_scrcpy_audio_gain(double gain) | void | Save scrcpyAudioGain, then emit scrcpyAudioGainChanged |
save_scrcpy_audio_duck_enabled(bool enabled) | void | Save scrcpyAudioDuckEnabled, then emit scrcpyAudioDuckEnabledChanged |
save_scrcpy_audio_duck_level(double level) | void | Save scrcpyAudioDuckLevel, then emit scrcpyAudioDuckLevelChanged |
save_scrcpy_audio_gain_db(double db), save_scrcpy_audio_duck_level_db(double db) | void | Save from dB (clamped to −12…+18 and −40…−6, kAudio*Db); the linear factor is what's stored |
save_scrcpy_phone_screen_off(bool off) | void | Save scrcpyPhoneScreenOff, then emit scrcpyPhoneScreenOffChanged |
save_scrcpy_display_dpi(int dpi), save_scrcpy_bit_rate_usb(int mbps), save_scrcpy_bit_rate_wifi(int mbps), save_scrcpy_auto_wifi(bool enabled), save_scrcpy_bit_rate_auto(bool enabled) | void | Clamp and persist scrcpyDisplayDpi / scrcpyBitRateUsb / scrcpyBitRateWifi / scrcpyAutoWifi, then emit the matching changed signal |
save_scrcpy_wireless_address(QString address) | void | Persist scrcpyWirelessAddress (trimmed; no-op when unchanged), then emit scrcpyWirelessAddressChanged |
save_esp32_volume_enabled(bool enabled) | void | Save esp32VolumeEnabled, then emit esp32VolumeEnabledChanged |
save_esp32_volume_port(QString port) | void | Save esp32VolumePort, then emit esp32VolumePortChanged |
save_esp32_volume_step_size(double stepSize) | void | Save esp32VolumeStepSize, then emit esp32VolumeStepSizeChanged |
save_esp32_auto_reconnect(bool enabled) | void | Save esp32AutoReconnect, then emit esp32AutoReconnectChanged |
save_esp32_led_sleep_enabled(bool enabled) | void | Save esp32LedSleepEnabled, then emit esp32LedSleepEnabledChanged |
save_esp32_led_color_mode(QString mode) | void | Save esp32LedColorMode, then emit esp32LedColorModeChanged |
save_esp32_led_static_color(QString color) | void | Save esp32LedStaticColor, then emit esp32LedStaticColorChanged |
save_imu_enabled(bool enabled) | void | Save imuEnabled, then emit imuEnabledChanged |
save_sensor_emit_rate(int hz) | void | Save sensorEmitRate clamped to 10–120, then emit sensorEmitRateChanged (BerryIMUManager follows it) |
save_sensor_decimal_places(int places), save_sensor_temp_unit(QString unit), save_sensor_altitude_unit(QString unit) | void | Save and notify; places clamp to 0–4, a unit other than F/C or m/ft is ignored |
save_sensor_card_visible(QString card, bool visible) | void | Show or hide one Sensors page readout (an unknown card name is ignored); emits sensorCardsChanged |
save_gesture_sensor_enabled(bool enabled) | void | Save gestureSensorEnabled, then emit gestureSensorEnabledChanged |
save_gesture_action(QString gesture, QString action) | void | Map one gesture (e.g. "RIGHT") to an action (e.g. "next_track"); emits gestureMappingChanged |
save_gesture_mapping(QVariantMap mapping) | void | Replace the whole gesture → action map |
reset_gesture_mapping() | void | Restore the default gesture map |
save_gesture_volume_step(int step) | void | Save gestureVolumeStep, then emit gestureVolumeStepChanged |
save_gesture_cooldown(int ms) | void | Save gestureCooldown, then emit gestureCooldownChanged |
toggle_pin_setting(QString settingId) | void | Pin/unpin a registry setting id on the Quick Panel; emits pinnedSettingsChanged |
reset_to_defaults() | void | Write the factory defaults, reload every cached member from them exactly as the constructor does (populateMembers()) and emit every property signal |
save_setting(QString key, QVariant value) | void | Generic write of any key; emits genericSettingChanged(key) (typed properties are not refreshed) |
get_setting(QString key) | QVariant | Raw value of any key (undefined when missing); use get_setting_with_default() for a fallback |
get_setting_with_default(QString key, QVariant defaultValue) | QVariant | Raw value of any key, or defaultValue when missing |
get_obd_parameter_enabled(QString parameter, bool defaultVal) | bool | Legacy per-PID enable flag (default true when never set); no longer drives polling |
get_supported_obd_parameters() | QVariantList | PID list saved by the last vehicle scan |
get_directory_history() | QVariantList | Same list as the directoryHistory property |
get_home_obd_parameters() | QVariantList | Same list as the homeOBDParameters property |
get_auto_play_on_startup() | bool | Returns the current autoPlayOnStartup value (same as the property) |
get_persist_shuffle_state() | bool | Returns the current persistShuffleState value (same as the property) |
get_last_shuffle_state() | bool | Returns the current lastShuffleState value (same as the property) |
get_last_played_song() | QString | Returns the current lastPlayedSong value (same as the property) |
get_last_played_position() | int | Returns the current lastPlayedPosition value (same as the property) |
get_last_played_playlist() | QString | Returns the current lastPlayedPlaylist value (same as the property) |
get_last_settings_section() | QString | Returns the current lastSettingsSection value (same as the property) |
get_window_state() | QString | Returns the current windowState value (same as the property) |
get_music_button_default_page() | QString | Returns the current musicButtonDefaultPage value (same as the property) |
get_return_to_library_after_selection() | bool | Returns the current returnToLibraryAfterSelection value (same as the property) |
flushPendingSave() | void | Write any coalesced (debounced) settings change to disk now; called at shutdown |
get_android_auto_enabled() | bool | Returns the current androidAutoEnabled value (same as the property) |
get_phone_mirror_enabled() | bool | Returns the current phoneMirrorEnabled value (same as the property) |
get_scrcpy_display_size() | QString | Returns the current scrcpyDisplaySize value (same as the property) |
get_scrcpy_audio_gain() | double | Returns the current scrcpyAudioGain value (same as the property) |
get_scrcpy_audio_duck_enabled() | bool | Returns the current scrcpyAudioDuckEnabled value (same as the property) |
get_scrcpy_audio_duck_level() | double | Returns the current scrcpyAudioDuckLevel value (same as the property) |
get_scrcpy_phone_screen_off() | bool | Returns the current scrcpyPhoneScreenOff value (same as the property) |
get_scrcpy_display_dpi(), get_scrcpy_bit_rate_usb(), get_scrcpy_bit_rate_wifi(), get_scrcpy_auto_wifi(), get_scrcpy_bit_rate_auto() | int (dpi, bit rates) / bool (auto Wi-Fi, auto bit rate) | Return the current values (same as the properties) |
get_scrcpy_wireless_address() | QString | Returns the current scrcpyWirelessAddress value (same as the property) |
get_esp32_volume_enabled() | bool | Returns the current esp32VolumeEnabled value (same as the property) |
get_esp32_volume_port() | QString | Returns the current esp32VolumePort value (same as the property) |
get_esp32_volume_step_size() | double | Returns the current esp32VolumeStepSize value (same as the property) |
get_esp32_auto_reconnect() | bool | Returns the current esp32AutoReconnect value (same as the property) |
get_esp32_led_sleep_enabled() | bool | Returns the current esp32LedSleepEnabled value (same as the property) |
get_esp32_led_color_mode() | QString | Returns the current esp32LedColorMode value (same as the property) |
get_esp32_led_static_color() | QString | Returns the current esp32LedStaticColor value (same as the property) |
get_show_waveform_visualizer() | bool | Returns the current showWaveformVisualizer value (same as the property) |
get_imu_enabled() | bool | Returns the current imuEnabled value (same as the property) |
get_gesture_sensor_enabled() | bool | Returns the current gestureSensorEnabled value (same as the property) |
get_gesture_mapping() | QString | Gesture → action map as a JSON string |
get_gesture_mapping_dict() | QVariantMap | Gesture → action map as a JS object |
is_setting_pinned(QString settingId) | bool | True if the id is in pinnedSettings |
get_settings_registry() | QString | JSON of the full settings registry (id, label, type, section, …) that drives the dynamic settings UI |
get_pinned_settings_metadata() | QString | JSON array of registry entries for every pinned setting |
get_settings_menu_visibility() | QString | Same JSON as the settingsMenuVisibility property |
is_settings_section_visible(QString section) | bool | Visibility of one settings section (default true) |
get_git_commit_hash() | QString | Short HEAD hash of the checkout; packaged builds fall back to the hash baked in by CMake ("unknown" only when neither exists) |
get_git_commit_date() | QString | Commit date of HEAD |
get_git_commit_message() | QString | Commit subject of HEAD |
Signals
| Signal | Description |
|---|---|
deviceNameChanged(QString value) | Emitted after deviceName is saved |
fontSettingChanged(QString value) | Emitted after fontSetting is saved |
themeSettingChanged(QString value) | Emitted after themeSetting is saved |
uiScaleChanged(float value) | Emitted after uiScale is saved |
colorTransitionMsChanged(int value) | Emitted after colorTransitionMs is saved |
songLengthTransitionChanged(bool value) | Emitted after songLengthTransition is saved |
showClockChanged(bool value) | Emitted after showClock is saved |
clockFormatChanged(bool value) | Notify for clockFormat24Hour |
clockShowSecondsChanged(bool value) | Emitted after clockShowSeconds is saved |
clockSizeChanged(int value) | Emitted after clockSize is saved |
clockSizeAutoChanged(bool value) | Emitted after clockSizeAuto is saved |
backgroundGridChanged(QString value) | Emitted after backgroundGrid is saved |
screenWidthChanged(int value) | Emitted after screenWidth is saved |
screenHeightChanged(int value) | Emitted after screenHeight is saved |
backgroundBlurRadiusChanged(int value) | Emitted after backgroundBlurRadius is saved |
bottomBarOrientationChanged(QString value) | Emitted after bottomBarOrientation is saved |
showBottomBarMediaControlsChanged(bool value) | Emitted after showBottomBarMediaControls is saved |
settingsLayoutStyleChanged(QString value) | Emitted after settingsLayoutStyle is saved |
albumArtColorsChanged() | Emitted after albumArtColors is saved |
settingsMenuVisibilityChanged() | A settings section was shown/hidden |
startUpVolumeChanged(QString value) | Notify for startUpVolume; carries the value as a string (QString::number(volume)) although the property is float |
currentVolumeChanged(int value) | Emitted after currentVolume is saved |
mediaFolderChanged(QString value) | Emitted after mediaFolder is saved |
autoPlayOnStartupChanged(bool value) | Emitted after autoPlayOnStartup is saved |
persistShuffleStateChanged(bool value) | Emitted after persistShuffleState is saved |
lastShuffleStateChanged(bool value) | Emitted after lastShuffleState is saved |
lastPlayedSongChanged(QString value) | Emitted after lastPlayedSong is saved |
lastPlayedPositionChanged(int value) | Emitted after lastPlayedPosition is saved |
lastPlayedPlaylistChanged(QString value) | Emitted after lastPlayedPlaylist is saved |
musicButtonDefaultPageChanged(QString value) | Emitted after musicButtonDefaultPage is saved |
returnToLibraryAfterSelectionChanged(bool value) | Emitted after returnToLibraryAfterSelection is saved |
showWaveformVisualizerChanged(bool value) | Emitted after showWaveformVisualizer is saved |
show3DButtonTiltChanged(bool value) | Emitted after show3DButtonTilt is saved |
show3DAlbumPreviewChanged(bool value) | Emitted after show3DAlbumPreview is saved |
roundedAlbumArtChanged(bool value) | Emitted after roundedAlbumArt is saved |
albumArtCornerRadiusChanged(int value) | Emitted after albumArtCornerRadius is saved |
showAlbumArtShadowChanged(bool value) | Emitted after showAlbumArtShadow is saved |
vinylRecordModeChanged(bool value) | Emitted after vinylRecordMode is saved |
audioOutputDeviceChanged(QString value) | Emitted after audioOutputDevice is saved |
albumArtTransitionChanged(QString value) | Emitted after albumArtTransition is saved |
backgroundOverlayOpacityChanged(int value) | Emitted after backgroundOverlayOpacity is saved |
sideCardOpacityChanged(double value) | Emitted after sideCardOpacity is saved |
sideCardAngleChanged(int value) | Emitted after sideCardAngle is saved |
buttonTiltDurationChanged(int value) | Emitted after buttonTiltDuration is saved |
textScrollSpeedChanged(int value) | Emitted after textScrollSpeed is saved |
obdBluetoothPortChanged(QString value) | Emitted after obdBluetoothPort is saved |
obdSavedAdaptersChanged() | Emitted after obdSavedAdapters is saved |
obdFastModeChanged(bool value) | Emitted after obdFastMode is saved |
obdAutoReconnectAttemptsChanged(int value) | Emitted after obdAutoReconnectAttempts is saved |
fuelTankCapacityChanged(float value) | Emitted after fuelTankCapacity is saved |
obdParametersChanged() | The per-PID enabled map changed (debounced after save_obd_parameter_enabled) |
homeOBDParametersChanged() | Home-screen PID list changed |
supportedOBDParametersChanged() | Vehicle-scan PID list changed |
directoryHistoryChanged() | Recent folders list changed |
customThemesChanged() | A custom theme was saved or deleted |
windowStateChanged(QString value) | Emitted after windowState is saved |
lastSettingsSectionChanged(QString value) | Emitted after lastSettingsSection is saved |
androidAutoEnabledChanged(bool value) | Emitted after androidAutoEnabled is saved |
phoneMirrorEnabledChanged(bool value) | Emitted after phoneMirrorEnabled is saved |
scrcpyDisplaySizeChanged(QString value) | Emitted after scrcpyDisplaySize is saved |
scrcpyAudioGainChanged(double value) | Emitted after scrcpyAudioGain is saved |
scrcpyAudioDuckEnabledChanged(bool value) | Emitted after scrcpyAudioDuckEnabled is saved |
scrcpyAudioDuckLevelChanged(double value) | Emitted after scrcpyAudioDuckLevel is saved |
scrcpyPhoneScreenOffChanged(bool value) | Emitted after scrcpyPhoneScreenOff is saved |
scrcpyDisplayDpiChanged(int), scrcpyBitRateUsbChanged(int), scrcpyBitRateWifiChanged(int), scrcpyAutoWifiChanged(bool), scrcpyBitRateAutoChanged(bool) | Emitted after the matching setting is saved |
scrcpyWirelessAddressChanged(QString value) | Emitted after scrcpyWirelessAddress is saved |
esp32VolumeEnabledChanged(bool value) | Emitted after esp32VolumeEnabled is saved |
esp32VolumePortChanged(QString value) | Emitted after esp32VolumePort is saved |
esp32VolumeStepSizeChanged(double value) | Emitted after esp32VolumeStepSize is saved |
esp32AutoReconnectChanged(bool value) | Emitted after esp32AutoReconnect is saved |
esp32LedSleepEnabledChanged(bool value) | Emitted after esp32LedSleepEnabled is saved |
esp32LedColorModeChanged(QString value) | Emitted after esp32LedColorMode is saved |
esp32LedStaticColorChanged(QString value) | Emitted after esp32LedStaticColor is saved |
imuEnabledChanged(bool value) | Emitted after imuEnabled is saved |
gestureSensorEnabledChanged(bool value) | Emitted after gestureSensorEnabled is saved |
gestureMappingChanged() | Gesture → action map changed |
gestureVolumeStepChanged(int value) | Emitted after gestureVolumeStep is saved |
gestureCooldownChanged(int value) | Emitted after gestureCooldown is saved |
pinnedSettingsChanged() | Quick Panel pins changed |
genericSettingChanged(QString key) | Emitted by save_setting(key) with the key that was written |
AssetStore — assetStore
src/util/assetstore.{h,cpp}. Supplies filesystem URLs for assets that external loaders such as Assimp cannot read from an Android APK.
| Signature | Returns | Description |
|---|---|---|
localUrl(QString relativePath) | QUrl | Resolve a path under frontend/assets/. Android copies it to AppDataLocation/asset-cache/ when absent or a different size; desktop returns the source file URL. Empty input or any .. returns an empty URL. Android also returns an empty URL if extraction fails; desktop can return a URL for a missing file. |
Clock — clock
src/managers/clock.h — QML context property clock
Formats the bottom-bar clock from the current time and the clock settings.
Invokables & Slots
| Signature | Returns | Description |
|---|---|---|
update_time() | void | Force a re-evaluation now (the timer calls this every second) |
Signals
| Signal | Description |
|---|---|
timeChanged(QString timeStr) | New formatted time text. Evaluated every second by a QTimer, emitted only when the text changes (once a minute without seconds). Honours settingsManager.clockFormat24Hour / clockShowSeconds; emits "" once when showClock turns off. The same text is readable as the time property, which this signal notifies |
DiagnosticsManager — diagnosticsManager
src/managers/diagnosticsmanager.h — QML context property diagnosticsManager
Field diagnostics behind Settings → Device → Diagnostics: read the recent log and get the log files off the head unit without a laptop.
Properties
| Property | Type | Access | Notify signal | Description |
|---|---|---|---|---|
logDir | QString | read-only, constant | — | <app data dir>/logs |
appVersion | QString | read-only, constant | — | App version (CMake PROJECT_VERSION) |
backendName | QString | read-only, constant | — | Always "C++"; included in the clipboard / export header |
deviceInfo | QString | read-only, constant | — | OS, architecture and host name summary |
lastExportPath | QString | read-only | lastExportPathChanged | Folder the last exportLogs() wrote ("" until it has run) |
canOpenFolder | bool | read-only, constant | — | Desktop true, mobile false (no file manager to open) |
Invokables & Slots
| Signature | Returns | Description |
|---|---|---|
recentLogLines(int maxLines) | QString | Tail of the main log followed by the error log (last 256 KB read); default 200 lines |
copyLogsToClipboard(int maxLines) | bool | Header (version, backendName, device, time) plus recentLogLines() onto the clipboard; default 400 lines |
exportLogs() | QString | Copy every octave*.log* file plus device-info.txt into <Downloads>/OCTAVE-logs-<timestamp>/ (Android: /storage/emulated/0/Download). Returns the folder, "" on failure |
openLogFolder() | bool | Desktop: open the log folder in the OS file manager |
licenseText(QString name) | QString | Full text of LICENSE or NOTICE (compiled-in resources); "" for any other name |
Signals
| Signal | Description |
|---|---|
lastExportPathChanged() | After a successful exportLogs() |
NetworkManager — networkManager
src/managers/networkmanager.h — QML context property networkManager
WiFi / internet status, GitHub update check and git-based self-update. All probes run asynchronously.
Properties
| Property | Type | Access | Notify signal | Description |
|---|---|---|---|---|
networkName | QString | read-only | networkNameChanged | Current WiFi SSID / interface name |
isConnected | bool | read-only | isConnectedChanged | Internet reachability; polled every 30 s online, backing off to 2 min after repeated misses |
updateStatus | QString | read-only | updateStatusChanged | Result of checkForUpdates(): "checking", "up-to-date", "update-available", "ahead", "diverged" or "error" ("" before the first check) |
updateMessage | QString | read-only | updateMessageChanged | Human-readable update status line |
remoteCommit | QString | read-only | remoteCommitChanged | Short hash of the newest commit on the GitHub main branch |
remoteCommitMsg | QString | read-only | remoteCommitMsgChanged | Subject of that remote commit |
workingTreeDirty | bool | read-only | workingTreeDirtyChanged | True when the checkout has uncommitted changes; the UI hides self-update because it hard-resets the tree |
selfUpdateStatus | QString | read-only | selfUpdateStatusChanged | State of applySelfUpdate(): "idle", "fetching", "restart-required" or "error" |
selfUpdateMessage | QString | read-only | selfUpdateMessageChanged | Progress / result text of the self-update |
canSelfUpdate | bool | read-only | canSelfUpdateChanged | True when running from a git checkout with git available |
Invokables & Slots
| Signature | Returns | Description |
|---|---|---|
refreshNetwork() | void | Re-probe connectivity and network name now |
checkForUpdates() | void | Query the GitHub API for the latest main commit and compare with local HEAD (same / behind / ahead / diverged) |
applySelfUpdate() | void | git fetch + git reset --hard origin/main (+ cmake configure + build), with rollback on failure; refuses when the tree is dirty |
restartApp() | void | Relaunch the app with the same arguments and quit |
shutdown_device() | void | Quit OCTAVE and power the OS off (systemctl poweroff / sudo shutdown / shutdown /s) |
cleanup() | void | Stop timers and kill any running git process (called at exit) |
Signals
| Signal | Description |
|---|---|
networkNameChanged(QString name) | Notify for networkName |
isConnectedChanged(bool connected) | Notify for isConnected |
updateStatusChanged(QString status) | Notify for updateStatus |
updateMessageChanged(QString message) | Notify for updateMessage |
remoteCommitChanged(QString commit) | Notify for remoteCommit |
remoteCommitMsgChanged(QString msg) | Notify for remoteCommitMsg |
workingTreeDirtyChanged(bool dirty) | Notify for workingTreeDirty |
selfUpdateStatusChanged(QString status) | Notify for selfUpdateStatus |
selfUpdateMessageChanged(QString message) | Notify for selfUpdateMessage |
canSelfUpdateChanged(bool canUpdate) | Notify for canSelfUpdate |
VolumeController — volumeController
src/managers/volumecontroller.h — QML context property volumeController
The single entry point for volume and mute. Every slider, knob and gesture calls applyVolume(percent) or toggleMute(); the quadratic curve and its inverse live here (toLinear / toPercent), and UIs bind to percent / muted / effectivelyMuted without doing curve math. Rules (tests/cpp/tst_volumecontroller.cpp): raising the volume while muted unmutes, from any input (judged on the request, so “up” at 100 % counts); a UI that echoes percentChanged back into applyVolume does not re-dispatch; lowering or re-applying the same level stays muted; the mute toggle at 0 % restores the startup volume and unmutes instead of toggling; presses within 150 ms are ignored. Mute is in memory only.
Properties
| Property | Type | Access | Notify signal | Description |
|---|---|---|---|---|
percent | int | read-only | percentChanged | The live volume 0–100 (also settingsManager.currentVolume). Sliders bind to this |
muted | bool | read/write | mutedChanged | Mute flag; main.cpp forwards it to mediaManager.setMuted, phoneMirrorManager.setMuted and the ESP32 mute LED |
effectivelyMuted | bool | read-only | effectivelyMutedChanged | muted or percent == 0: what the mute icons show |
Invokables & Slots
| Signature | Returns | Description |
|---|---|---|
applyVolume(int percent) | void | Clamp to 0–100, store in percent and settingsManager.currentVolume, compute the quadratic-curve linear value and fan it out to every output: media and phone mirror take the linear value, the ESP32 LED ring takes the percent. Dispatched via volumeApplied. If the percent went up while muted, also unmutes |
toLinear(float percent) | float | Pure curve math ((percent/100)^2) for QML previews; does not dispatch |
toPercent(float linear) | int | The inverse (round(sqrt(linear) * 100)), e.g. for the startup volume setting, which is stored linear. toPercentStatic is the inline C++ form SettingsManager uses |
setMuted(bool muted) | void | Mute or unmute without touching the level |
toggleMute() | void | The mute button (screen, ESP32 knob, gesture, command server). At 0 % restores toPercent(startUpVolume) and unmutes; otherwise flips. 150 ms debounce |
Signals
| Signal | Description |
|---|---|
percentChanged(int percent), mutedChanged(bool muted), effectivelyMutedChanged() | Property notifies; volumeApplied still fires on every applyVolume() except an echo of percentChanged, percentChanged only on a real change |
volumeApplied(int percent, float linear) | After every applyVolume(): clamped percent and the linear 0–1 value. main.cpp connects it to mediaManager.setVolume(linear), phoneMirrorManager.setVolume(linear) and esp32VolumeManager.send_volume_update(percent) |
MediaManager — mediaManager
src/managers/mediamanager.h — QML context property mediaManager
Local playback (MP3 / M4A / MP4 / FLAC / OGG / OPUS / WAV) via QMediaPlayer: library scanning, playlists as sub-folders, tag metadata (TagLib — title / artist / album for every format, WAV via its ID3 chunk), cover extraction (MP3, M4A/MP4, FLAC, Ogg Vorbis and Opus; WAV has no art), palette extraction and playback-resume state.
Invokables & Slots
| Signature | Returns | Description |
|---|---|---|
connect_settings_manager(QObject *) | void | No-op; the SettingsManager is wired with setSettingsManager() in src/appcore.cpp |
_save_playback_state() | void | Write song / position / playlist to settings now (Main.qml calls it on close) |
_clear_temp_files() | void | Delete the extracted cover-art temp files |
flush_metadata_store() | void | Write the persistent tag cache (<appdata>/metadata_cache.json) if it changed; called at shutdown |
play_file(QString filename) | void | Play a file from the current playlist by filename (initialises the shuffled/ordered queue on first call) |
next_track() | void | Skip to the next track in the queue |
previous_track() | void | Go to the previous track |
pause() | void | Pause playback |
toggle_play() | void | Play/pause; starts the first track if nothing is loaded |
setMuted(bool muted) | void | Follows volumeController.mutedChanged (wired in src/appcore.cpp); call volumeController.toggleMute() from QML |
toggle_shuffle() | void | Toggle shuffle and rebuild the queue |
setVolume(float volume) | void | Set the linear output volume 0.0–1.0 (called by VolumeController, not QML) |
setDucking(float factor) | void | Temporarily attenuate the output (1.0 = none, 0.1 = -20 dB) while the mirrored phone makes sound; independent of the user volume |
set_position(int positionMs) | void | Seek to a position in ms |
extract_colors_from_image(QString imagePath) | void | Extract an Album Art Capture palette from a local path or file:// URL, including phone album art |
extract_colors_from_album_art(QString filename) | void | Run the palette extraction for a file; the result goes to settingsManager.set_album_art_colors (or albumColorsExtracted without a settings manager) |
scan_library(bool resetDisplayNames) | void | Rescan the library root for audio files and playlists; resetDisplayNames also rebuilds cleaned display names |
set_library_root(QString path) | void | Point the library at another folder (must exist) and rescan |
strip_filenames() | void | Rename files in the library to cleaned display names (progress via scanProgress) |
select_playlist(QString name) | void | Make a playlist (sub-folder or "All Music") the active song list |
create_playlist(QString name) | bool | Create a playlist sub-folder; false if it exists or the name is invalid |
delete_playlist(QString name) | bool | Delete a playlist sub-folder and its files |
move_song_to_playlist(QString filename, QString targetPlaylist) | bool | Move a file into another playlist folder |
delete_song(QString filename) | bool | Delete a file from disk and the library; emits songDeleted |
play_file_at_path(QString filePath) | bool | Rescan, select the playlist containing the absolute path and play it |
invalidate_stats_cache() | void | Force the album / artist / total-duration stats to be recomputed on next read |
get_media_files(bool emitSignal) | QStringList | Filenames in the library; emitSignal=false suppresses the mediaListChanged emit |
get_formatted_duration(QString filename) | QString | Track duration as M:SS (reads tags on demand) |
get_band(QString filename) | QString | Artist from tags |
get_album(QString filename) | QString | Album from tags |
get_album_art(QString filename) | QString | file:// URL of the extracted cover (cached per album), "" if none |
get_current_file() | QString | Filename of the current track |
get_next_track_name() | QString | Filename that next_track() would play |
get_previous_track_name() | QString | Filename that previous_track() would play |
get_next_track_album_art() | QString | Cover URL of the next track |
get_previous_track_album_art() | QString | Cover URL of the previous track |
get_neighbor_album_arts() | QStringList | [previousArt, nextArt] for the 3D side cards |
is_playing() | bool | Currently playing |
is_paused() | bool | Currently paused |
get_duration() | float | Duration in ms of the current track |
get_position() | float | Position in ms |
is_muted() | bool | Mirror of volumeController.muted |
getVolume() | float | Linear output volume 0.0–1.0 |
is_shuffled() | bool | Shuffle state |
get_playlist_names() | QStringList | All playlist names including the combined "All Music" |
get_current_playlist_name() | QString | Active playlist name |
get_current_song_list() | QStringList | Filenames of the active playlist in queue order |
get_movable_playlist_names() | QStringList | Playlist names excluding "All Music" (targets for move_song_to_playlist) |
get_song_playlist_name(QString filename) | QString | Playlist folder the file actually lives in (not "All Music"); empty if unknown |
get_song_file_path(QString filename) | QString | Absolute path for a filename in the library ("" if unknown) |
get_full_file_path(QString filename) | QString | Absolute path, but only when the file exists on disk |
get_display_name(QString filename) | QString | Cleaned display name (tags/stripped filename) for a file |
get_default_media_dir() | QString | The default music folder for this OS |
get_media_folder_name() | QString | Basename of the current media folder |
pick_browser_library() | bool | Browser demo: opens the browser's folder picker and makes the folder picked the library for the visit (true); elsewhere false, so the caller opens a folder dialog |
get_total_duration() | QString | Total library duration, formatted |
get_album_count() | int | Unique album count |
get_artist_count() | int | Unique artist count |
sort_media_files(QString sortColumn, bool ascending) | QStringList | Return the active list sorted by "title", "artist" or "album" |
Signals
| Signal | Description |
|---|---|
playbackStateChanged(int state) | QMediaPlayer playback state as an int: 0 stopped, 1 playing, 2 paused (forwarded from QMediaPlayer::playbackStateChanged) |
playStateChanged(bool playing) | true when playing, false on pause/stop |
currentMediaChanged(QString filename) | Filename of the track that just started |
mediaListChanged(QStringList files) | The visible song list (current playlist) was replaced |
durationChanged(qint64 durationMs) | Track length in ms became known |
positionChanged(qint64 positionMs) | Playback position in ms (100 ms timer) |
metadataChanged(QString title, QString artist, QString album) | Title / artist / album of the current track loaded from tags |
durationFormatChanged(QString formatted) | Duration formatted as M:SS |
volumeChanged(float volume) | Linear output volume 0.0–1.0 |
shuffleStateChanged(bool shuffled) | Shuffle toggled |
totalDurationChanged(QString formatted) | Library total duration recalculated (formatted string) |
albumCountChanged(int count) | Unique album count recalculated |
artistCountChanged(int count) | Unique artist count recalculated |
playlistsChanged() | Playlist (sub-folder) set changed after a scan / create / delete |
currentPlaylistChanged(QString name) | Active playlist name changed |
scanProgress(QString message) | Terminal-style progress line during library scans, filename stripping and errors |
albumColorsExtracted(QString jsonColors) | JSON palette extracted from the current album art, emitted only when no SettingsManager is wired (tests); the app's path is settingsManager.albumArtColors |
songDeleted(QString filename) | A file was removed by delete_song() |
audioOutputsChanged() | The output list or the output in use changed; notify of the read-only properties audioOutputs (QVariantList of {id, name, isDefault}) and currentAudioOutput (QString, the name of the output playing) |
AudioAnalyzer — audioAnalyzer
src/managers/audioanalyzer.h — QML context property audioAnalyzer
Pre-computes FFT bar levels for the waveform visualizer and replays them at the playback position. It decodes to 8 kHz mono and bin the 0–4 kHz magnitudes into 96 logarithmic bands, each at least one FFT bin wide (the first bands are linear until the log curve overtakes them, so no bar is permanently 0).
Properties
| Property | Type | Access | Notify signal | Description |
|---|---|---|---|---|
levels | QVariantList | read-only | fftDataChanged | The current 96 bar levels (0–8) |
numBars | int | constant | — | 96 |
Invokables & Slots
| Signature | Returns | Description |
|---|---|---|
setViewActive(bool active) | void | A view showing the bars is on screen: the QML UI's consumer, setDemand("view", active). Nothing runs and no file is decoded while no consumer wants the bars |
setDemand(QString consumer, bool wanted) | void | A frontend's bars are on screen under its own consumer name; runs while any consumer wants them; released when its service connection closes |
setPlaying(bool), setLocalFile(QString path), setPosition(qint64 ms) | void | What is playing, fed by main.cpp from nowPlaying.playing, MediaManager::currentMediaChanged and MediaManager::positionChanged (local bars change at that 100 ms rate) |
analyze_file(QString filePath) | void | Decode a file and pre-compute FFT levels in 100 ms chunks off the GUI thread (QtConcurrent / ThreadPoolExecutor); a file requested mid-analysis is queued |
update_position(float positionSeconds) | void | Select the chunk for a playback position (seconds) and emit fftDataChanged when active |
set_active(bool active) | void | Enable/disable emission; disabling emits a row of zeros to clear the visualizer |
get_current_levels() | QVariantList | Last emitted bar levels |
get_num_bars() | int | Number of bars (96) |
set_live(bool live) | void | Switch between file analysis and live phone PCM (main.cpp, from nowPlaying.source); resets the live buffers on a mode change |
update_live() | void | Publish spectrum levels for the phone audio currently being heard; run by the analyzer's own 50 ms timer while live and shown |
feed_live_pcm(QByteArray pcm, int bufferedMs) | void | Slot connected in main.cpp. Receives s16le 48 kHz stereo PCM handed to the output sink and its queued delay from PhoneMirrorManager.audioPlayed |
is_analyzed() | bool | True when FFT data for the current file is ready |
clear() | void | Drop the pre-computed data and reset the levels |
Signals
| Signal | Description |
|---|---|
fftDataChanged(QVariantList levels) | 96 bar levels (ints 0–8) for the current playback position |
analysisComplete() | FFT pre-computation of the requested file finished |
analysisStarted() | FFT pre-computation started |
OBDManager — obdManager
src/managers/obdmanager.h — QML context property obdManager
OBD-II diagnostics over an ELM327 adapter: USB / Bluetooth serial on desktop (worker thread), BLE GATT on Android. Base parameters have named signals; extended multi-value parameters use obdParameterChanged(QString, float). PID numbers, command names and decoders come from ELM327Protocol::buildPidTable(), which carries all 82 mode-01 commands plus ELM_VOLTAGE; polling is demand-driven: only the PIDs that registered consumers (dashboard, Parameter Cards, Home, editor, settings list) ask for via setParameterDemand() are polled, plus an RPM heartbeat, filtered to what this session's scan found. Changes apply on a live connection without reconnecting (the worker's poll list is swapped via updatePidsToWatch(), on Android via rebuildAndroidPollList()). See OBD Manager → Which PIDs Get Polled.
OctaveOBDBridge.java (JNI); the QML-facing API is identical. Connect chains the 0100 through 01C0 supported-PID bitmaps so default PIDs above 0x20 (fuel level, module voltage, oil temperature) and the multi-value PIDs are polled, the first poll slot reads the VIN, and the bridge requests BLUETOOTH_CONNECT on the first Connect tap (the former requestAndroidBluetoothPermission() helper is gone).Properties
| Property | Type | Access | Notify signal | Description |
|---|---|---|---|---|
connected | bool | read-only | connectionStatusChanged | True while the adapter is connected and initialised |
connectionStatus | QString | read-only | connectionStatusChanged | Last connectionStatusChanged value ("Disconnected" initially; "Connecting", "Connected", "No Vehicle", "Reconnecting", "Connection Failed", "Stale Connection", "Device Lost", "Error", …); cached from the signal |
connectionState | ConnectionState | read-only | connectionStatusChanged | Disconnected / Connecting / Connected / NoVehicle / Failed, classified from connectionStatus (OBDManager::stateFor); QML: import OCTAVE.OBD 1.0, OBDManager.Connecting |
connectionDetail | QString | read-only | connectionStatusDetailChanged | Last connectionStatusDetailChanged message, bound by the Connection card |
connectionProgress | int | read-only | connectionProgressChanged | Last connectionProgressChanged percent (0–100) |
connectionLog | QStringList | read-only | connectionLogChanged | The last 60 connection log lines, oldest first; OBDHome and the OBD settings page bind it (clearConnectionLog() empties it) |
availableAdapters | QVariantList | read-only | availableAdaptersChanged | Rich [{name, identifier, kind}, …] list bound by the OBD settings page; kind is ble, serial-rfcomm, serial-usb, com, tty or serial. On Android the list is the phone's bonded devices from OctaveOBDBridge.bondedDevices(), named "<name> · <MAC>"; it stays empty until BLUETOOTH_CONNECT is granted, which the bridge requests on the first Connect tap |
vin | QString | read-only | vinChanged | 17-character VIN read at connect (Mode 09 PID 02); "" when the vehicle reports none |
vehicleMake | QString | read-only | vinChanged | Manufacturer decoded from the VIN's WMI; "" if unknown |
vehicleModelYear | int | read-only | vinChanged | Model year from VIN position 10; 0 if unknown |
vehicleSupportedParameters | QStringList | read-only | vehicleSupportedParametersChanged | This session's scan result in catalog ids (base commands + multi-value signals, plus any alias whose target is in it, e.g. IGNITION_TIMING with TIMING_ADVANCE); [] until the scan finishes and again after disconnect. Never loaded from the persisted supportedOBDParameters |
vehicleScanComplete | bool | read-only | vehicleSupportedParametersChanged | True once this session's scan finished (even with an empty result); false while disconnected |
Invokables & Slots
| Signature | Returns | Description |
|---|---|---|
isConnected() | bool | Property getter for connected |
is_connected() | bool | snake_case alias of isConnected() used by the QML |
connect_obd() | void | Start a connection attempt (named to avoid QObject::connect) |
reconnect() | void | Tear down and reconnect, resetting the attempt counter |
force_connect() | void | Reset the attempt counter, clear any wedged in-progress flag and connect now |
close() | void | Disconnect, stop scanning/watchdog timers and suppress auto-reconnect |
reset_connection() | void | Alias of force_connect() |
get_connection_status() | QString | Same value as the connectionStatus property — the connectionStatusChanged vocabulary; the settings-page status dot keys its colour map on it |
getConnectionStatus() | QString | camelCase alias backing the connectionStatus property |
refresh_ports() | void | Run the port discovery sweep now |
get_available_ports() | QVariantList | Sweep and return the plain port list |
scan() | void | Trigger a discovery sweep (alias of refresh_ports); the name QML uses |
availableAdapters() | QVariantList | Getter for the availableAdapters property |
connect_to_adapter(QString identifier) | void | Single entry point of the OBD settings page: normalise the identifier (MAC / /dev path / COM), save it, rfcomm bind on Linux when given a MAC, then force_connect() |
connect_direct(QString mac, int channel = 1) | void | Linux: connect over a direct Bluetooth RFCOMM socket; saves rfcomm://<MAC>[/<channel>] as the OBD port and goes through connect_to_adapter() (no bind) |
platform_hint() | QString | "linux" / "macos" / "windows" / "android" / "ios"; drives hint copy only, never structural branching |
setParameterDemand(QString consumer, QStringList paramIds) | void | Replace one consumer's wanted parameter ids (empty list removes it). The poll set is the union of all consumers, filtered to vehicleSupportedParameters, plus the RPM heartbeat (ELM_VOLTAGE if the car has no RPM); BerryIMU and unknown ids are ignored. Debounced 250 ms, applied live |
setBackgroundDemand(QString consumer, QStringList paramIds) | void | Low-priority demand: same rules minus the heartbeat and the foreground PIDs, polled one PID per pass of the foreground list (OBDManager::backgroundPidsToWatch()). Used by TripRecorder for its sweep |
scan_vehicle() | void | Probe every known PID on the worker thread (Android: answer from the supported-PID bitmaps read at connect time); progress via scanProgressChanged/scanOutputChanged, result via scanCompleteChanged |
is_scanning() | bool | A vehicle scan is running |
get_all_parameter_names() | QStringList | Every single-value PID command name (COOLANT_TEMP, RPM, …), sorted, from the decoder table; multi-value ids are not included |
get_supported_commands() | QVariantList | PID names found by the last scan |
enable_scanned_parameters(QVariantList paramNames) | void | Set the legacy per-PID enable keys for a list of PID names (no longer affects polling) |
enable_all_supported() | void | Set the legacy per-PID enable keys for every PID the last scan found (no longer affects polling) |
enter_diagnostic_mode() | void | Pause live polling so DTC commands get the bus |
exit_diagnostic_mode() | void | Resume live polling (after a short delay) |
is_diagnostic_mode() | bool | Diagnostic mode active |
read_dtc() | void | Read stored DTCs (Mode 03) → dtcCodesChanged, dtcCountChanged. This and the four DTC / status / freeze-frame slots below run on the desktop worker thread; on Android (no worker) they log once and return |
read_current_dtc() | void | Read pending DTCs (Mode 07) |
read_status() | void | Read MIL state and DTC count (Mode 01 PID 01) → milStatusChanged, dtcCountChanged |
clear_dtc() | void | Clear codes (Mode 04) → dtcClearResult |
read_freeze_frame() | void | Read freeze frame 0 (Mode 02): the code that stored it and its recorded values → freezeFrameChanged |
read_vin() | void | Read the VIN again (Mode 09 PID 02) → vinChanged; runs on Android too (next poll slot) |
get_dtc_codes() | QVariantList | Last DTC list |
get_dtc_count() | int | Last DTC count |
get_mil_status() | bool | Last MIL state |
get_freeze_frame() | QVariantList | Last freeze-frame list |
coolantTemp() | float | Last value of the coolantTemp PID (0 until read) |
voltage() | float | Last value of the voltage PID (0 until read) |
engineLoad() | float | Last value of the engineLoad PID (0 until read) |
throttlePosition() | float | Last value of the throttlePosition PID (0 until read) |
intakeTemp() | float | Last value of the intakeTemp PID (0 until read) |
timingAdvance() | float | Last value of the timingAdvance PID (0 until read) |
massAirFlow() | float | Last value of the massAirFlow PID (0 until read) |
speedMPH() | float | Last value of the speedMPH PID (0 until read) |
rpm() | float | Last value of the rpm PID (0 until read) |
airFuelRatio() | float | Last value of the airFuelRatio PID (0 until read) |
fuelLevel() | float | Last value of the fuelLevel PID (0 until read) |
intakeManifoldPressure() | float | Last value of the intakeManifoldPressure PID (0 until read) |
shortTermFuelTrim() | float | Last value of the shortTermFuelTrim PID (0 until read) |
longTermFuelTrim() | float | Last value of the longTermFuelTrim PID (0 until read) |
oxygenSensorVoltage() | float | Last value of the oxygenSensorVoltage PID (0 until read) |
fuelPressure() | float | Last value of the fuelPressure PID (0 until read) |
engineOilTemp() | float | Last value of the engineOilTemp PID (0 until read) |
ignitionTiming() | float | Last value of the ignitionTiming PID (0 until read) |
refresh_values() | void | Re-emit the current value of every watched PID (no-op when disconnected); the home screen calls it after homeOBDParametersChanged |
open_bluetooth_settings() | void | Desktop: no-op. Android: opens the system Bluetooth settings |
set_target_address(QString address) | void | Persist the identifier as obdBluetoothPort; the saved-port watcher then reconnects |
set_auto_reconnect(bool enabled) | void | Deprecated no-op — use settingsManager.obdAutoReconnectAttempts |
set_connection_timeout(int timeoutSeconds) | void | Connection timeout in seconds (clamped 5–60); it also bounds the vehicle-verify RPM request after ELM327 init (no obdConnectionTimeout settings key exists) |
check_device_presence() | bool | Check whether the configured port exists; emits devicePresenceChanged |
Signals
| Signal | Description |
|---|---|
coolantTempChanged(float value) | Coolant Temp — Mode 1 PID 0x05 (°C) |
voltageChanged(float value) | Control Module Voltage — Mode 1 PID 0x42 (V) |
engineLoadChanged(float value) | Engine Load — Mode 1 PID 0x04 (%) |
throttlePositionChanged(float value) | Throttle Position — Mode 1 PID 0x11 (%) |
intakeAirTempChanged(float value) | Intake Air Temp — Mode 1 PID 0x0F (°C) |
timingAdvanceChanged(float value) | Timing Advance — Mode 1 PID 0x0E (°) |
massAirFlowChanged(float value) | MAF — Mode 1 PID 0x10 (g/s) |
speedMPHChanged(float value) | Speed — Mode 1 PID 0x0D (mph) |
rpmChanged(float value) | RPM — Mode 1 PID 0x0C (rpm) |
airFuelRatioChanged(float value) | Air/Fuel Ratio — Mode 1 PID 0x44: commanded equivalence ratio (lambda) × 14.7 |
fuelLevelChanged(float value) | Fuel Level — Mode 1 PID 0x2F (%) |
intakeManifoldPressureChanged(float value) | Intake Manifold Pressure — Mode 1 PID 0x0B (kPa) |
shortTermFuelTrimChanged(float value) | Short Term Fuel Trim 1 — Mode 1 PID 0x06 (%) |
longTermFuelTrimChanged(float value) | Long Term Fuel Trim 1 — Mode 1 PID 0x07 (%) |
oxygenSensorVoltageChanged(float value) | O2 Sensor B1S1 — Mode 1 PID 0x14 (V) |
fuelPressureChanged(float value) | Fuel Pressure — Mode 1 PID 0x0A (kPa) |
engineOilTempChanged(float value) | Oil Temp — Mode 1 PID 0x5C (°C) |
ignitionTimingChanged(float value) | Same value as timingAdvanceChanged (both are emitted from PID 0x0E) |
runTimeChanged(float value) | Run Time — Mode 1 PID 0x1F (s) |
distanceWithMILChanged(float value) | Distance w/ MIL — Mode 1 PID 0x21 (km) |
fuelRailPressureChanged(float value) | Fuel Rail Pressure (vac) — Mode 1 PID 0x22 (kPa) |
fuelRailPressureDirectChanged(float value) | Fuel Rail Pressure (direct) — Mode 1 PID 0x23 (kPa) |
barometricPressureChanged(float value) | Barometric Pressure — Mode 1 PID 0x33 (kPa) |
ambientAirTempChanged(float value) | Ambient Air Temp — Mode 1 PID 0x46 (°C) |
relativeThrottlePosChanged(float value) | Relative Throttle Pos — Mode 1 PID 0x45 (%) |
absoluteThrottlePosBChanged(float value) | Throttle Pos B — Mode 1 PID 0x47 (%) |
acceleratorPosChanged(float value) | Accelerator Pos D — Mode 1 PID 0x49 (%) |
catalystTempB1S1Changed(float value) | Catalyst Temp B1S1 — Mode 1 PID 0x3C (°C) |
catalystTempB1S2Changed(float value) | Catalyst Temp B1S2 — Mode 1 PID 0x3E (°C) |
evapVaporPressureChanged(float value) | Evap Vapor Pressure — Mode 1 PID 0x32 |
shortFuelTrim2Changed(float value) | Short Fuel Trim 2 — Mode 1 PID 0x08 (%) |
longFuelTrim2Changed(float value) | Long Fuel Trim 2 — Mode 1 PID 0x09 (%) |
o2SensorB1S2Changed(float value) | O2 B1S2 — Mode 1 PID 0x15 |
o2SensorB2S1Changed(float value) | O2 B2S1 — Mode 1 PID 0x18 (V) |
o2SensorB2S2Changed(float value) | O2 B2S2 — Mode 1 PID 0x19 (V) |
distanceSinceCodesCleared(float value) | Distance Since Codes Cleared — Mode 1 PID 0x31 (km) |
warmupsSinceCodesCleared(float value) | Warmups Since Codes Cleared — Mode 1 PID 0x30 |
absoluteLoadChanged(float value) | Absolute Load — Mode 1 PID 0x43 (%) |
commandedEGRChanged(float value) | Commanded EGR — Mode 1 PID 0x2C (%) |
egrErrorChanged(float value) | EGR Error — Mode 1 PID 0x2D |
ethanoPercentChanged(float value) | Ethanol Percent — Mode 1 PID 0x52 |
o2SensorB1S3Changed(float value) | O2 B1S3 — Mode 1 PID 0x16 (V) |
o2SensorB1S4Changed(float value) | O2 B1S4 — Mode 1 PID 0x17 (V) |
o2SensorB2S3Changed(float value) | O2 B2S3 — Mode 1 PID 0x1A (V) |
o2SensorB2S4Changed(float value) | O2 B2S4 — Mode 1 PID 0x1B (V) |
o2S1WRVoltageChanged(float value) | Wide-range O2 sensor 1 voltage — Mode 1 PID 0x24 (bytes C/D × 8 / 65535 V) |
o2S2WRVoltageChanged(float value) | Wide-range O2 sensor 2 voltage — Mode 1 PID 0x25 (bytes C/D × 8 / 65535 V) |
o2S3WRVoltageChanged(float value) | Wide-range O2 sensor 3 voltage — Mode 1 PID 0x26 (bytes C/D × 8 / 65535 V) |
o2S4WRVoltageChanged(float value) | Wide-range O2 sensor 4 voltage — Mode 1 PID 0x27 (bytes C/D × 8 / 65535 V) |
o2S5WRVoltageChanged(float value) | Wide-range O2 sensor 5 voltage — Mode 1 PID 0x28 (bytes C/D × 8 / 65535 V) |
o2S6WRVoltageChanged(float value) | Wide-range O2 sensor 6 voltage — Mode 1 PID 0x29 (bytes C/D × 8 / 65535 V) |
o2S7WRVoltageChanged(float value) | Wide-range O2 sensor 7 voltage — Mode 1 PID 0x2A (bytes C/D × 8 / 65535 V) |
o2S8WRVoltageChanged(float value) | Wide-range O2 sensor 8 voltage — Mode 1 PID 0x2B (bytes C/D × 8 / 65535 V) |
o2S1WRCurrentChanged(float value) | Wide-range O2 sensor 1 current — Mode 1 PID 0x34 (bytes C/D / 256 − 128 mA) |
o2S2WRCurrentChanged(float value) | Wide-range O2 sensor 2 current — Mode 1 PID 0x35 (bytes C/D / 256 − 128 mA) |
o2S3WRCurrentChanged(float value) | Wide-range O2 sensor 3 current — Mode 1 PID 0x36 (bytes C/D / 256 − 128 mA) |
o2S4WRCurrentChanged(float value) | Wide-range O2 sensor 4 current — Mode 1 PID 0x37 (bytes C/D / 256 − 128 mA) |
o2S5WRCurrentChanged(float value) | Wide-range O2 sensor 5 current — Mode 1 PID 0x38 (bytes C/D / 256 − 128 mA) |
o2S6WRCurrentChanged(float value) | Wide-range O2 sensor 6 current — Mode 1 PID 0x39 (bytes C/D / 256 − 128 mA) |
o2S7WRCurrentChanged(float value) | Wide-range O2 sensor 7 current — Mode 1 PID 0x3A (bytes C/D / 256 − 128 mA) |
o2S8WRCurrentChanged(float value) | Wide-range O2 sensor 8 current — Mode 1 PID 0x3B (bytes C/D / 256 − 128 mA) |
catalystTempB2S1Changed(float value) | Catalyst Temp B2S1 — Mode 1 PID 0x3D (°C) |
catalystTempB2S2Changed(float value) | Catalyst Temp B2S2 — Mode 1 PID 0x3F (°C) |
throttlePosCChanged(float value) | Throttle Pos C — Mode 1 PID 0x48 (%) |
acceleratorPosEChanged(float value) | Accelerator Pos E — Mode 1 PID 0x4A (%) |
acceleratorPosFChanged(float value) | Accelerator Pos F — Mode 1 PID 0x4B (%) |
throttleActuatorChanged(float value) | Throttle Actuator — Mode 1 PID 0x4C (%) |
evaporativePurgeChanged(float value) | Evaporative Purge — Mode 1 PID 0x2E (%) |
fuelRailPressureAbsChanged(float value) | Fuel Rail Pressure Abs — Mode 1 PID 0x59 (kPa) |
fuelInjectTimingChanged(float value) | Fuel Inject Timing — Mode 1 PID 0x5D |
fuelRateChanged(float value) | Fuel Rate — Mode 1 PID 0x5E (L/h) |
runTimeMILChanged(float value) | Run Time MIL — Mode 1 PID 0x4D (min) |
timeSinceDTCClearedChanged(float value) | Time Since DTC Cleared — Mode 1 PID 0x4E (min) |
maxMAFChanged(float value) | Max MAF — Mode 1 PID 0x50 |
fuelTypeChanged(float value) | Fuel Type — Mode 1 PID 0x51 |
evapVaporPressureAbsChanged(float value) | Evap Vapor Pressure Abs — Mode 1 PID 0x53 |
evapVaporPressureAltChanged(float value) | Evap Vapor Pressure Alt — Mode 1 PID 0x54 |
shortO2TrimB1Changed(float value) | Short O2 Trim B1 — Mode 1 PID 0x55 (%) |
longO2TrimB1Changed(float value) | Long O2 Trim B1 — Mode 1 PID 0x56 (%) |
shortO2TrimB2Changed(float value) | Short O2 Trim B2 — Mode 1 PID 0x57 (%) |
longO2TrimB2Changed(float value) | Long O2 Trim B2 — Mode 1 PID 0x58 (%) |
relativeAccelPosChanged(float value) | Relative Accel Pos — Mode 1 PID 0x5A (%) |
hybridBatteryRemainingChanged(float value) | Hybrid Battery — Mode 1 PID 0x5B |
elmVoltageChanged(float value) | ELM327 adapter supply voltage (V) from the adapter's own ATRV command (text reply such as 12.6V); polled like a PID |
connectionStatusChanged(QString status) | Status text changed |
connectionStatusDetailChanged(QString detail) | Detailed status message |
connectionLostUnexpectedly(QString reason) | A live link dropped on its own (not a user disconnect, port change, diagnostic-mode switch or shutdown); shown as the app-wide ErrorToast |
connectionProgressChanged(int progress) | Connection progress percent |
devicePresenceChanged(bool present) | Configured adapter detected / lost on the port scan |
availablePortsChanged(QVariantList ports) | Plain port-name list from the discovery sweep |
availableAdaptersChanged(QVariantList adapters) | Same sweep, rich {name, identifier, kind} shape |
supportedCommandsChanged(QVariantList commands) | PID names the vehicle answered during the scan |
scanProgressChanged(int progress, QString message) | Vehicle scan progress percent and message |
scanCompleteChanged(QVariantList supportedParams) | Vehicle scan finished; list of supported PID names |
vehicleSupportedParametersChanged() | vehicleSupportedParameters / vehicleScanComplete changed (scan finished, or reset on disconnect) |
scanOutputChanged(QString output) | Terminal-style scan output line |
dtcCodesChanged(QVariantList codes) | Stored DTCs read ([{code, description}, …]) |
dtcCountChanged(int count) | Number of stored DTCs |
milStatusChanged(bool status) | Check-engine lamp state |
dtcClearResult(bool success, QString message) | Clear-codes result (success, message) |
freezeFrameChanged(QVariantList data) | Freeze frame read: [{code, description, values: {paramId: value}}] |
obdParameterChanged(QString paramId, float value) | One value from a multi-value PID (0x61 and up: torque, boost, odometer, …); OBDParameterModel routes it by id |
parameterSampled(QString paramId, float value) | Every decoded reading, base or multi-value, by parameter id, on desktop and Android; the black box and the value store (paramStore) tap this |
vinChanged(QString vin) | VIN read at connect or by read_vin() |
connectionLogLineAppended(QString line) | One connection log line (kept in connectionLog) |
connectionLogChanged() | Notify for connectionLog |
ParameterCatalog — parameterCatalog
src/managers/parametercatalog.h — QML context property parameterCatalog
The one table of display metadata for the 129 values a gauge can show: 120 OBD ids (the python-obd command names OBDManager emits on parameterSampled, which settings and dashboard files store), one alias (IGNITION_TIMING carries TIMING_ADVANCE) and the 8 BerryIMU readings. Decoders stay in ELM327Protocol; tests/cpp/tst_parametercatalog.cpp keeps the catalog's OBD ids equal to the decoders' and checks that frontend/OBDParameterModel.qml serves it unchanged (it has no table of its own; this one was generated from it and compared field by field before the switch). C++ reads it through the static ParameterCatalog::all() / find(id) / imuIds() / obdIds().
Properties
| Property | Type | Access | Notify signal | Description |
|---|---|---|---|---|
parameters | QVariantList | constant | — | Every parameter, in display order, as {id, title, shortTitle, unit, min, max, kind, decimals, source, aliasOf, group}; group is the system it belongs to (one of groups). kind is percentage | temperature | voltage | pressure | bidirectional | numeric (what WidgetCatalog matches widgets on); decimals is 0 or 1 for text readouts; source is "obd" or "imu"; shortTitle may be empty |
displayParameters | QVariantList | read-only | displayParametersChanged | The same rows in the gauges' units (setDisplayUnits): unit, min and max converted, plus displayScale / displayOffset (value = raw × scale + offset; 1 / 0 when unchanged). Temperatures (every kind temperature row) by gaugeTempUnit, SPEED by gaugeSpeedUnit, ALTITUDE by sensorAltitudeUnit; OBDParameterModel serves gauges from it. parameters stays in each parameter's own unit |
groups | QStringList | constant | — | The group values in the order pickers list them: Engine, Fuel & air, Temperatures, Electrical, Speed & motion, Emissions, Diagnostics, Sensors (the IMU) |
Invokables & Slots
| Signature | Returns | Description |
|---|---|---|
info(QString id) | QVariantMap | One entry as in parameters (hash lookup); empty map for an unknown id |
contains(QString id) | bool | Whether the id is in the catalog |
toDisplay(QString id, QVariant value) | QVariant | A value in the gauges' units (the reference OBDParameterModel's scale/offset follow); invalid or non-numeric values and unknown ids pass through |
setDisplayUnits(QString temp, QString speed, QString altitude) | void | C++ only: src/appcore.cpp feeds SettingsManager's units; emits displayParametersChanged on a change |
ParameterStore — paramStore
src/managers/parameterstore.h — QML context property paramStore, a QQmlPropertyMap
The latest value of every catalog id, one notifying key per id: bind paramStore.RPM or paramStore[id] and the binding re-evaluates only when that id changes; an id with no reading yet is undefined. src/main.cpp feeds it from obdManager.parameterSampled and the eight berryIMU signals; QML writes are rejected. Writes are coalesced: values land together every 33 ms (kFlushMs). A catalog alias (IGNITION_TIMING) is written whenever its target arrives. OBDParameterModel.liveValue(id) reads it for every gauge and card. Tests: tests/cpp/tst_parameterstore.cpp.
Invokables & Slots
| Signature | Returns | Description |
|---|---|---|
get(QString id) | QVariant | Current value, or invalid / undefined if none yet |
snapshot() | QVariantMap | Every id that has a value → its value |
ingest(QString id, float value) | void | Record a reading (lands at the next fold). Wired in src/appcore.cpp; tests and simulators call it too |
Signals
| Signal | Description |
|---|---|
changed(QStringList ids) | After each fold: the ids whose values were written. Per-key bindings do not need it; it is for consumers that want the batch |
NowPlaying — nowPlaying
src/managers/nowplaying.h — QML context property nowPlaying
What is playing right now and the controls for it, whichever source owns OCTAVE's music: the phone (while mirroring, once phoneMirrorManager.mediaActive), Android Auto (built-in receiver) or OCTAVE's own player. It is also the arbiter: one source at a time, and only a user action changes it (Which source plays the music). Frontends bind to this instead of choosing between mediaManager and phoneMirrorManager themselves. The phone reports its position only now and then, so this owns the one clock that extrapolates it (every 500 ms, only while the phone plays). Values are raw: placeholder text such as "Nothing playing" is the frontend's choice (hasTrack). It also holds the hand-off (the phone taking the controls pauses local playback; a local track starting hands them back with releaseMedia(true), pausing the phone) and the Album Art Capture rule (the theme follows the live source's art). Tests: tests/cpp/tst_nowplaying.cpp.
Properties
| Property | Type | Access | Notify signal | Description |
|---|---|---|---|---|
source | QString | read-only | sourceChanged | "local", "phone" or "androidauto" |
playing | bool | read-only | playingChanged | The live source is playing |
hasTrack, title, artist, album | bool, QString | read-only | trackInfoChanged | Local: display name, artist, album of the current file. Phone: the media session's metadata (hasTrack false with no media app) |
artUrl | QString | read-only | artUrlChanged | Album art URL, "" when none (a file on the head unit; service clients fetch GET /art) |
artRevision | int | read-only | artUrlChanged | Goes up by one on every art change: the cache key for GET /art |
durationMs, positionMs | int | read-only | durationChanged, positionChanged | Local: the player's 10 Hz reports. Phone: each report, extrapolated every 500 ms while playing. 0 duration = unknown |
canSeek, canShuffle | bool | read-only | capabilitiesChanged | canShuffle is false on the phone (its shuffle belongs to its app) |
shuffled | bool | read-only | shuffledChanged | Local shuffle state |
Invokables & Slots
| Signature | Returns | Description |
|---|---|---|
togglePlay(), next(), previous() | void | Routed to the live source (the gesture sensor uses these too) |
seek(int positionMs) | void | When canSeek; the position shows at once and the source confirms it |
toggleShuffle() | void | Local only; does nothing while the phone or Android Auto is the source |
useLocal() | void | Hand the music back to OCTAVE's own player (the Media Room's source pill); the phone or Android Auto pauses, nothing plays until asked |
extractArtColors() | void | Theme colours from the live source's art now (Media Room's "Save as Theme"). The Album Art Capture rule calls it on its own: while that theme is selected, whenever artUrl changes or the theme is chosen |
Signals
| Signal | Description |
|---|---|
trackChanged() | Another track or another source (for track-change animations); not emitted for play/pause, seeks or position |
ShiftLightManager — shiftLightManager
src/managers/shiftlightmanager.h — QML context property shiftLightManager
The RPM shift light's rules, fed by OBDManager::rpmChanged (wired in src/appcore.cpp). A flag is an RPM band (rpmLow..rpmHigh, both inclusive) with a colour and effects, stored in the rpm_flags setting as a JSON string. When bands overlap the last matching flag wins. A legacy flag ({rpm} with no band) is migrated once on load to rpmLow = rpm, rpmHigh = rpm_max_rpm and written back. The flash timer runs here, so every frontend sees the same phase: a UI binds to active, activeFlag and lit and draws. All rpm_* settings are re-read when any of them changes. Tests: tests/cpp/tst_shiftlightmanager.cpp.
Properties
| Property | Type | Access | Notify signal | Description |
|---|---|---|---|---|
enabled | bool | read-only | settingsChanged | rpm_shift_light_enabled (default true). Off means active is always false |
showOnHomeCard | bool | read-only | settingsChanged | rpm_show_on_home_card (default true) |
maxRpm | int | read-only | settingsChanged | rpm_max_rpm (default 8000): the redline, and the high bound given to migrated legacy flags |
fullScreenFlashOpacity | double | read-only | settingsChanged | rpm_fullscreen_flash_opacity (default 0.5): the fallback for flags without their own opacity |
lampSize, glowSize, glowIntensity | double | read-only | settingsChanged | rpm_shift_light_size (0.5), rpm_glow_size (0.6), rpm_glow_intensity (0.6) |
colorTransitionSpeed | int | read-only | settingsChanged | rpm_color_transition_speed in ms (default 100) |
pulseEnabled | bool | read-only | settingsChanged | rpm_pulse_enabled (default false) |
flags | QVariantList | read-only | flagsChanged | The flags as stored, already migrated to bands. Write with setFlags() |
rpm | double | read-only | rpmChanged | Last RPM seen |
active | bool | read-only | activeFlagChanged | A flag's band contains the RPM, and the light is enabled and not suppressed |
activeIndex | int | read-only | activeFlagChanged | Index into flags, or -1 |
activeFlag | QVariantMap | read-only | activeFlagChanged | The active flag with every field filled in: color (#FF0000), flash (false), flashSpeed (100 ms), fullScreenFlash (false), fullScreenFlashOpacity (the global one), auraFlash (false), auraFlashOpacity (0.6), auraSpread (0.18). Empty when not active |
lit | bool | read-only | litChanged | The light is on right now: active, and in the on phase if the flag flashes. A new flag always starts lit |
suppressed | bool | read/write | suppressedChanged | In-memory override that forces the light dark without touching the saved setting (the dev screenshot stories use it) |
Invokables & Slots
| Signature | Returns | Description |
|---|---|---|
setRpm(double rpm) | void | Feed a reading; re-matches the band. Connected to obdManager.rpmChanged in main.cpp |
setFlags(QVariantList flags) | void | Save the flags to rpm_flags (the RPM editor on OBDMenu calls this); applied live |
Signals
| Signal | Description |
|---|---|
settingsChanged() | Any rpm_* setting was re-read |
flagsChanged() | The stored flags changed |
activeFlagChanged() | Another flag (or none) became active, or the active one was edited. Not emitted while the RPM stays inside the same band |
litChanged() | On/off, including every flash toggle |
rpmChanged(), suppressedChanged() | Property notifies |
TripRecorder — tripRecorder
src/managers/triprecorder.h — QML context property tripRecorder
The black box: every OBD reading of every drive into <config>/history/blackbox.sqlite, one CSV + JSON per trip under history/trips/. Trip detection and coalescing on the GUI thread, SQLite (TripStore) on its own thread; results come back as signals. See OBD Manager → Black Box and TripHistory.qml.
Properties
| Name | Type | Notify | Description |
|---|---|---|---|
enabled | bool | enabledChanged | Mirrors blackBoxEnabled |
storeReady | bool | storeReadyChanged | The database opened; while false nothing is recorded and lastError says why |
recording | bool | recordingChanged | A trip is open |
currentTripId | qint64 | recordingChanged | Id (= start time in ms) of the open trip, 0 when idle |
currentTripStartMs | qint64 | recordingChanged | Start of the open trip |
currentSampleCount | int | currentSampleCountChanged | Samples stored this trip (updated on each flush) |
currentTripElapsedS | int | currentTripElapsedSChanged | Seconds since the trip being recorded started, 0 when none; moves once a second (the recorder's 1 s tick) |
trips | QVariantList | tripsChanged | Summaries newest first: {id, startMs, endMs, startIso, endIso, durationS, open, clean, vin, make, modelYear, sampleCount, paramCount, distanceMiles, maxSpeed, maxRpm, maxCoolant, maxOilTemp, minVoltage, maxVoltage, fuelStart, fuelEnd, fuelUsedPct, eventCount, file, summaryFile} (no per-parameter list) |
historyDir / tripsDir | QString | historyDirChanged | <config>/history and its trips/ |
storageBytes | qint64 | storageBytesChanged | Database (+ WAL) plus trip files |
canOpenFolder | bool | constant | False on Android / iOS |
lastExportPath | QString | lastExportPathChanged | CSV path of the last successful export |
lastError | QString | lastErrorChanged | Last store error (database could not be opened, write failed) |
Invokables & Slots
| Method | Returns | Description |
|---|---|---|
recordSample(QString paramId, float value) | void | One reading (wired from obdManager.parameterSampled); starts a trip on RPM > 0, otherwise dropped until a trip is open |
setConnected(bool), setVehicleParameters(QStringList), setVehicleIdentity(QString vin, QString make, int year), noteEvent(QString kind, QString detail), setEnabled(bool) | void | Inputs attach(OBDManager*) wires from the OBD manager and the settings |
endTripNow() | void | Close the open trip here (trip_end event "ended by user"); a new one starts on the next RPM > 0 |
refresh() | void | Re-list trips and storage |
requestDetail(qint64 id) | void | → detailReady with the full summary (params, events); live for the open trip |
requestSeries(qint64 id, QString paramId, int maxPoints = 400, double fromS = -1, double toS = -1) | void | → seriesReady with [{t, v, lo, hi}]: seconds since start, bucket average, min, max. fromS / toS (seconds since start, negative = whole trip) window the query so a zoomed chart gets maxPoints buckets over just that span |
requestHealthTrends(int days = 30) | void | → healthTrendsReady: the closed trips that started in the last days days (0 = all), oldest first, as {days, fromMs, toMs, tripCount, metrics, params}. Each of metrics is a health number (warmupMin, settledCoolant, idleVoltage, chargingVoltage, idleRpm, ltft1, ltft2; see Health across drives) with title, unit, decimals, description, the usual range bandLo / bandHi (absent: none), rows [{id, startMs, v}], latest, recent (last three drives), baseline (the drives before, when there are three), status (ok / watch / none) and a note for a person. params: every parameter recorded in the window, {id, title, unit, trips}, most trips first |
requestParamTrend(QString paramId, int days = 30) | void | → paramTrendReady with [{id, startMs, v, lo, hi}]: one parameter's average, min and max per trip over the same window, from the stored summaries |
deleteTrip(qint64 id) | void | Rows and files; the open trip is ended first |
clearHistory() | void | Everything but the trip being recorded |
exportTrip(qint64 id) | void | Copy the CSV + JSON into Downloads/OCTAVE-trips/ → exportFinished |
openHistoryFolder() | bool | Desktop: open tripsDir in the file manager |
Signals
| Signal | Description |
|---|---|
tripStarted(qint64 id) | A trip opened |
tripEnded(qint64 id, QVariantMap summary) | Closed, summarised and written out |
detailReady(qint64 id, QVariantMap detail), seriesReady(qint64 id, QString paramId, QVariantList points), exportFinished(qint64 id, QString path), healthTrendsReady(int days, QVariantMap result), paramTrendReady(QString paramId, int days, QVariantList rows) | Answers to the requests above (path empty on failure) |
enabledChanged(bool), storeReadyChanged(), recordingChanged(), currentSampleCountChanged(), tripsChanged(), historyDirChanged(), storageBytesChanged(), lastExportPathChanged(), lastErrorChanged(), currentTripElapsedSChanged() | Property notifiers |
liveTripChanged(qint64 id) | New samples of the trip being recorded reached the store, at most once per 5 s: a view showing that trip re-requests its detail and series (the requests queue behind the write) |
OBDAdapterFinder — obdAdapterFinder
src/managers/obdadapterfinder.h — QML context property obdAdapterFinder
Finds, pairs (answering the PIN itself through a BlueZ agent), trusts and connects a Bluetooth OBD-II adapter for frontend/OBDAdapterSheet.qml. See OBD Manager → Adapter Finder.
Properties
| Name | Type | Notify | Description |
|---|---|---|---|
supported | bool | constant | Discovery + pairing + connect work here (desktop Linux). False elsewhere; the sheet then falls back to obdManager.availableAdapters |
canScan | bool | constant | startScan() can look for adapters: supported on desktop; in the browser demo, whether the browser has Web Bluetooth (false in Firefox, Safari and Linux Chromium without its flag, where the sheet hides "Pick Bluetooth Adapter" and offers only the simulated car) |
bluetoothOn | bool | bluetoothOnChanged | A controller exists and is powered |
scanning | bool | scanningChanged | Discovery running (15 s) |
devices | QVariantList | devicesChanged | [{address, name, rssi, kind: "classic"|"ble"|"dual", likelyObd, paired, connectable, note}]; likely adapters first, then by signal (rssi 0 = cached, not heard this scan) |
busyAddress | QString | busyChanged | Adapter being paired / connected, "" when idle |
status | QString | statusChanged | Human-readable progress or outcome |
statusIsError | bool | statusChanged | status describes a failure |
Invokables & Slots
| Signature | Returns | Description |
|---|---|---|
startScan() | void | Clear the list and run Classic + LE discovery; no-op while pairing |
stopScan() | void | Stop discovery (the sheet calls it on close; a pairing under way continues) |
connectAdapter(QString address) | void | Pair with PINs 1234 / 0000 / 6789 / 1111, trust, look up the SPP channel, then obdManager.connect_direct() and a named saved-adapter chip |
cancel() | void | Cancel pairing / channel lookup / scan and clear the status |
powerOn() | void | Power the Bluetooth controller on |
Signals
| Signature | Description |
|---|---|
bluetoothOnChanged() | Controller powered on/off |
scanningChanged() | Discovery started or stopped |
devicesChanged() | The list changed (device found, signal updated, paired) |
busyChanged() | busyAddress changed |
statusChanged() | status / statusIsError changed |
ESP32VolumeManager — esp32VolumeManager
src/managers/esp32volumemanager.h — QML context property esp32VolumeManager
Wireless rotary encoder over USB serial (115200 baud). Encoder ticks and button presses arrive as signals; src/appcore.cpp routes them through VolumeController, and pushes volume (send_volume_update), mute (send_mute_state) and the LED ring colour (send_theme_color: album-art accent in "theme" mode, esp32LedStaticColor in "static" mode, cyan fallback) back to the knob — including a full push 500 ms after the knob connects. ResourceError, DeviceNotFoundError, PermissionError, ReadError, WriteError and a rejected keepalive write are treated as a lost link and trigger auto-reconnect.
get_available_ports() returns an empty list, get_connection_status() "Disconnected", get_connection_detail() "Not available on mobile", is_connected() false) and the five signals. QML still guards with typeof esp32VolumeManager.Properties
| Property | Type | Access | Notify signal | Description |
|---|---|---|---|---|
connected | bool | read-only | connectionStatusChanged | Serial link to the knob is open |
connectionStatus | QString | read-only | connectionStatusChanged | "Connected" / "Disconnected" / "Connecting" / "Error" |
connectionDetail | QString | read-only | connectionDetailChanged | Human-readable detail (port, last error) |
availablePorts | QVariantList | read-only | availablePortsChanged | Serial ports found by the last scan, one {port, label, description, isEsp32S3} map each, ESP32-S3 first then by port, the OBD port left out; the Accessories page's picker binds it |
Invokables & Slots
| Signature | Returns | Description |
|---|---|---|
connect_device() | void | Open the configured (or auto-detected VID 303A) port at 115200 baud |
disconnect_device() | void | Close the serial port |
get_available_ports() | QVariantList | Scan and return availablePorts |
is_connected() | bool | Link verified: an ESP32-S3 port is open, or another port has sent a knob command (only then does the manager write) |
get_connection_status() | QString | Status text |
get_connection_detail() | QString | Detail text |
refresh_ports() | void | Rescan ports now |
reset_reconnect_attempts() | void | Zero the auto-reconnect attempt counter so reconnects resume |
cleanup() | void | Stop timers and disconnect at exit |
Signals
| Signal | Description |
|---|---|
connectionStatusChanged(QString status) | Status text changed |
connectionDetailChanged(QString detail) | Detail message changed |
availablePortsChanged(QVariantList ports) | Port scan finished |
volumeChangeRequested(float delta) | Encoder turned: ± esp32VolumeStepSize × ticks; main routes it through VolumeController |
muteToggleRequested() | Encoder button pressed |
BerryIMUManager — berryIMU
src/managers/berryimumanager.h — QML context property berryIMU
BerryIMU v3 (LSM6DSL accel/gyro, LIS3MDL mag, BMP388 baro) over I2C with a Madgwick AHRS on a worker thread (accel + gyro fusion only; the magnetometer feeds the heading readout). The Android build reads Qt Sensors instead. Five consecutive failed I2C block reads mark the bus dead: status "Error", then the usual retry / back-off. A permission-denied bus (EACCES / EPERM) logs a warning once (permission denied on /dev/i2c-N - add the user to the i2c group; not retrying), emits "Disconnected" and does not retry.
setEnabled() follows imuEnabled like desktop (a persisted “off” stops the sensors when the SettingsManager is attached), connectionStatusChanged reports "Connected", "Unavailable" or "Disabled", setEmitRate() clamps to the same 10–120 Hz, and orientationChanged is synthesised as a yaw-stripped quaternion from the tared pitch / roll so CarMenu sees one convention.Properties
| Property | Type | Access | Notify signal | Description |
|---|---|---|---|---|
connected | bool | read-only | connectionStatusChanged | The sensor read loop is running |
hasTemperature | bool | read-only | hasTemperatureChanged | A barometric temperature reading is available: always true on desktop; on Android it flips once Qt Sensors reports a value |
Invokables & Slots
| Signature | Returns | Description |
|---|---|---|
getConnectionStatus() | QString | Status text |
setEnabled(bool enabled) | void | Start/stop the sensor (runtime only, not saved). The manager calls it on settingsManager.imuEnabledChanged: QML saves with save_imu_enabled instead |
isEnabled() | bool | Enabled flag |
setEmitRate(int hz) | void | Signal rate in Hz (clamped 10–120); the value is kept and applied to every worker the manager creates, so a call before start or across a retry is not lost. Driven by settingsManager.sensorEmitRate from startup |
emitRate() | int | The rate in Hz currently requested (C++ only) |
setActive(bool active) | void | Driven by setDemand: true while any consumer wants the IMU; false drops the worker to a 5 Hz idle rate |
setDemand(QString consumer, bool wanted) | void | A page showing IMU readings registers while on screen (frontend/ImuDemand.qml, consumer = its objectName); full rate while any consumer wants it |
calibrateTare() | void | Take the current orientation as the zero reference (CarMenu ZERO). Raises a request that the read thread services on its next pass; ignored with a log line when the loop is not running |
resetTare() | void | Clear the tare (CarMenu RESET); serviced on the read thread while running, applied immediately otherwise |
cleanup() | void | Stop the worker thread and close the I2C bus |
Signals
| Signal | Description |
|---|---|
orientationChanged(float w, float x, float y, float z) | Tared, yaw-stripped quaternion (w, x, y, z) from the Madgwick AHRS for the 3D vehicle model (Android: built from the tared pitch / roll) |
pitchChanged(float pitch) | Pitch in degrees |
rollChanged(float roll) | Roll in degrees |
headingChanged(float heading) | Compass heading 0–360 |
altitudeChanged(float altitude) | Barometric altitude in metres |
accelMagnitudeChanged(float mag) | Total acceleration in g |
lateralGChanged(float g) | Side-to-side g |
longitudinalGChanged(float g) | Front-to-back g |
baroTempChanged(float temp) | Barometer temperature |
connectionStatusChanged(QString status) | "Connected" / "Disconnected" / "Error" (Android: "Connected" / "Unavailable" / "Disabled") |
hasTemperatureChanged(bool has) | Notify for hasTemperature |
GestureManager — gestureSensor
src/managers/gesturemanager.h — QML context property gestureSensor
PAJ7620U2 I2C gesture sensor on a worker thread; gestures map to media actions. Directional / rotation / wave gestures are confirmed by a second register read 20 ms later: the sample is accepted when the re-read agrees or is empty (the sensor clears its flags on read) and discarded when it shows a different gesture. Five consecutive failed register reads mark the bus dead: status "Error", then retry / back-off. A permission-denied bus (EACCES / EPERM) logs a warning once (permission denied on /dev/i2c-N - add the user to the i2c group; not retrying), emits "Disconnected" and does not retry.
connectionStatus property, every slot below (getConnectionStatus() returns "Disabled on mobile", getGestureAction() "none", isEnabled() false, setCooldown(int)) and the three signals including gestureDetected and connectionStatusChanged(QString).Properties
| Property | Type | Access | Notify signal | Description |
|---|---|---|---|---|
connectionStatus | QString | read-only | connectionStatusChanged | "Connected" / "Disconnected" (mobile stub: "Disabled on mobile"); read by the Accessories widget |
gestures | QStringList | constant | — | The nine gestures the sensor reports, in settings-page order (gesturevocabulary.h) |
actions | QVariantList | constant | — | {key, label} per action a gesture can trigger, none ("Disabled") last; the mobile stub has both |
Invokables & Slots
| Signature | Returns | Description |
|---|---|---|
getConnectionStatus() | QString | Status text |
setGestureAction(QString gesture, QString action) | void | Map a gesture to an action and persist it |
getGestureAction(QString gesture) | QString | Action mapped to a gesture |
resetMappingToDefaults() | void | Restore the default map (RIGHT→next_track, UP/CLOCKWISE→volume_up, FORWARD/WAVE→play_pause, BACKWARD→mute_toggle, …) |
setEnabled(bool enabled) | void | Start/stop the sensor (runtime only, not saved). The manager calls it on settingsManager.gestureSensorEnabledChanged: QML saves with save_gesture_sensor_enabled instead |
setCooldown(int ms) | void | Minimum ms between accepted gestures (runtime; follows gestureCooldownChanged). cooldownMs() reads it back (C++ only) |
isEnabled() | bool | Enabled flag |
cleanup() | void | Stop the worker thread and close the I2C bus |
Signals
| Signal | Description |
|---|---|
gestureDetected(QString gesture) | Raw gesture: RIGHT, LEFT, UP, DOWN, FORWARD, BACKWARD, CLOCKWISE, COUNTER-CLOCKWISE, WAVE |
actionTriggered(QString action) | Mapped action: next_track, previous_track, volume_up, volume_down, play_pause, mute_toggle (handled in main) |
connectionStatusChanged(QString status) | Sensor connection state changed |
AndroidAutoManager — androidAutoManager
src/managers/androidautomanager.h — QML context property androidAutoManager
Android Auto with two backends, picked by the androidAutoBackend setting. Built-in ("native", implemented in src/managers/androidautomanager_native.cpp over src/android_auto/): OCTAVE is the head unit and speaks the Android Auto Protocol to the phone's head unit server through an adb forward; see Built-in Android Auto. Google DHU ("dhu", the default): the window/capture integration is incomplete (no window handle is discovered and click forwarding is a stub), and no current QML consumes image://dhuframe/; see platform status. adb is found by the same discovery as Phone Mirror (bundled platform-tools → PATH → SDK locations; PhoneMirrorManager::findAdb()), and only its own tcp:5277 forward is removed on setup / cleanup so a live mirror tunnel survives.
backend, nativeAvailable, nativeState, nativeStatus, phoneName, videoStreaming, certificateDir, videoSink, startNative, stopNative, injectTouch, setViewVisible, backendChanged, nativeStateChanged, videoStreamingChanged, videoSinkChanged) is marked Q_CLASSINFO("octave.experimental", ...): it is published in docs/api/androidAutoManager.json but may change without a protocol version bump.isDhuInstalled / isDhuEmbedded (constant false), launchDhuSeamless() (false), closeDhu(), the dhuEmbeddedChanged, error, dhuWindowReady signals, and the built-in backend's names with inert constant values (backend "dhu", nativeAvailable false, nativeState "idle", empty nativeStatus / phoneName, videoStreaming false, videoSink null; startNative() returns false, the other invokables do nothing). A desktop build without OpenSSL or FFmpeg has the full class with nativeAvailable false, backend always "dhu", and startNative() setting error ("This build has no native Android Auto").Properties
| Property | Type | Access | Notify signal | Description |
|---|---|---|---|---|
isDhuInstalled | bool | read-only, constant | — | Google Desktop Head Unit binary found in the Android SDK |
dhuPath | QString | read-only, constant | — | Path of the DHU binary |
isDhuEmbedded | bool | read-only | dhuEmbeddedChanged | Manager considers the DHU running/embedded; this alone does not prove its window is displayed |
dhuWindowHandle | int | read-only | dhuWindowReady | Currently a process ID or 0 rather than a discovered native window handle |
dhuCapture | QObject* | read-only, constant | — | The DhuCapture object streaming frames to image://dhuframe/ |
backend | QString | read-only | backendChanged | Experimental. "native" or "dhu" from the androidAutoBackend setting; "dhu" whenever nativeAvailable is false |
nativeAvailable | bool | read-only, constant | — | Experimental. Built with the native receiver (OCTAVE_HAVE_NATIVE_AA: OpenSSL + FFmpeg, desktop) |
nativeState | QString | read-only | nativeStateChanged | Experimental. idle | connecting | waitingForPhone | waitingForServer | needsCertificate | running | error (what each means) |
nativeStatus | QString | read-only | nativeStateChanged | Experimental. The state as a sentence for a person, including what to do |
phoneName | QString | read-only | nativeStateChanged | Experimental. Device name the phone reported in service discovery |
videoStreaming | bool | read-only | videoStreamingChanged | Experimental. The phone is sending video |
certificateDir | QString | read-only, constant | — | Experimental. <config dir>/android-auto: where headunit.crt + headunit.key are looked for when androidAutoCertPath is empty |
videoSink | QObject* | read/write | videoSinkChanged | Experimental. The QVideoSink decoded frames go to; AndroidAutoView.qml sets its VideoOutput's sink |
Invokables & Slots
| Signature | Returns | Description |
|---|---|---|
getDhuInstallInstructions() | QString | User-facing SDK Manager instructions |
launchDhuSeamless() | bool | Prepare the headunit server and adb forward, then launch DHU; embedding is incomplete (see above) |
sendDhuClick(int x, int y) | void | Forward a click through DhuCapture (currently a logged stub) |
closeDhu() | void | Stop capture and kill the DHU process |
cleanup() | void | At exit: end a built-in session with a bye-bye (waits up to 3 s), close the DHU, remove its tcp:5277 forward |
startNative() | bool | Experimental. Connect to the phone's head unit server and keep retrying (3 s while waiting for the phone, server or certificate; 8 s after an error) until stopNative(). False when it can't begin (nativeState says why); true when already connecting or running |
stopNative() | void | Experimental. Stop retrying; a running session ends with a bye-bye, the adb forward is removed, state returns to idle |
injectTouch(int pointerId, int action, double x, double y) | void | Experimental. A touch on the picture: pointerId from the touch area, action 0 down / 1 up / 2 move, x / y in 0..1 of the video. Only while running |
setViewVisible(bool visible) | void | Experimental. The view is on screen; false tells the phone to stop sending video (audio continues) |
Signals
| Signal | Description |
|---|---|
connectionProgress(QString message) | Launch progress text |
error(QString message) | Error message (DHU launch errors; also every failed built-in session) |
dhuWindowReady(int windowHandle) | DHU window found; carries the handle |
dhuEmbeddedChanged(bool embedded) | Embedding state changed |
backendChanged() | Experimental. androidAutoBackend was saved |
nativeStateChanged() | Experimental. nativeState, nativeStatus or phoneName changed |
videoStreamingChanged() | Experimental. videoStreaming changed |
videoSinkChanged() | Experimental. videoSink was set |
DhuCapture — dhuCapture
src/managers/androidautomanager.h — sub-object androidAutoManager.dhuCapture
Sub-object reached through androidAutoManager.dhuCapture; grabs the DHU window at 30 fps into the image provider. Not a context property of its own.
Properties
| Property | Type | Access | Notify signal | Description |
|---|---|---|---|---|
isCapturing | bool | read-only | captureStarted | Frame capture timer running |
windowHandle | int | read/write | — | Window being captured |
Invokables & Slots
| Signature | Returns | Description |
|---|---|---|
setWindowHandle(int hwnd) | void | Set the window to capture |
startCapture() | void | Start grabbing frames (30 fps) into the dhuframe image provider |
stopCapture() | void | Stop grabbing |
sendMouseClick(int x, int y) | void | Forward a click (currently a logged stub) |
Signals
| Signal | Description |
|---|---|
frameReady() | A new provider frame is available; the current QML does not display this provider |
captureStarted() | Capture started |
captureStopped() | Capture stopped |
error(QString message) | Capture error |
AirPlayManager — airPlayManager
src/managers/airplaymanager.h — QML context property airPlayManager
An iPhone over AirPlay: its mirrored screen and audio, and touch back through OCTAVE as a Bluetooth LE mouse. See iPhone (AirPlay). Settings: airPlayEnabled, airPlayName, airPlayRequirePin, airPlayResolution, airPlayTouchEnabled, airPlayTouchScale (generic keys).
Q_CLASSINFO("octave.experimental", ...): published in docs/api/airPlayManager.json but may change without a protocol version bump. Scope phone.OCTAVE_HAVE_AIRPLAY (libplist + OpenSSL + libavcodec, Linux) the class is the same with available false and state "unavailable". Without BlueZ, touchAvailable is false and touchState is "unavailable".Properties
| Property | Type | Access | Notify signal | Description |
|---|---|---|---|---|
available | bool | read-only, constant | — | Built with the AirPlay receiver |
enabled | bool | read-only | enabledChanged | airPlayEnabled (and available) |
state | QString | read-only | stateChanged | off | unavailable | error | waiting | pin | connected | mirroring | audio |
status | QString | read-only | stateChanged | The state as a sentence for a person |
receiverName | QString | read-only | stateChanged | What the iPhone lists (airPlayName) |
deviceName / deviceModel | QString | read-only | stateChanged | The connecting iPhone's name and model |
pin | QString | read-only | stateChanged | The PIN to show while a new iPhone pairs; empty otherwise |
mirroring / audioStreaming | bool | read-only | stateChanged | Screen mirroring / AirPlay audio is running |
frameWidth / frameHeight | int | read-only | frameSizeChanged | Size of the mirrored picture |
title / artist / album | QString | read-only | mediaChanged | Track info sent with AirPlay audio |
addresses | QStringList | read-only | stateChanged | This machine's IPv4 addresses on real interfaces (no container bridges or VPN tunnels) |
videoSink | QObject* | read/write | videoSinkChanged | The QVideoSink the mirrored picture goes to (AirPlayView.qml sets it) |
touchAvailable | bool | read-only, constant | — | Built with the Bluetooth mouse (BlueZ) |
touchEnabled | bool | read-only | touchChanged | airPlayTouchEnabled |
touchState | QString | read-only | touchChanged | unavailable | off | waiting | connected |
touchStatus / touchDevice | QString | read-only | touchChanged | The mouse's state as a sentence; the connected iPhone's name |
touchPairing | bool | read-only | touchChanged | The pairing window is open |
touchCalibrating | bool | read-only | touchChanged | Waiting for the calibration tap |
Invokables & Slots
| Signature | Returns | Description |
|---|---|---|
setEnabled(bool enabled) | void | Saves airPlayEnabled: start or stop advertising |
setTouchEnabled(bool enabled) | void | Saves airPlayTouchEnabled: start or stop the Bluetooth mouse |
pairTouch(bool open = true) | void | Accept a new Bluetooth pairing for three minutes (closes once an iPhone connects); false closes it |
injectTouch(int pointerId, int action, double x, double y) | void | A touch on the mirrored picture: action 0 down / 1 up / 2 move, x / y in 0..1. The first finger moves the pointer; only while the mouse is connected |
scroll(int dx, int dy) | void | Wheel steps (positive dy scrolls down) |
calibrateTouch() / cancelCalibration() | void | Home the pointer and move it 240 counts right and down; the next tap marks where it shows and sets airPlayTouchScale |
forgetDevices() | void | Forget every iPhone that paired with the PIN |
Signals
| Signal | Description |
|---|---|
enabledChanged(), stateChanged(), mediaChanged(), frameSizeChanged(), videoSinkChanged(), touchChanged() | Property notifications |
mirroringStarted() | An iPhone started mirroring (Main.qml opens the iPhone page) |
audioStreamingChanged(bool streaming) | AirPlay audio started or stopped |
PhoneMirrorManager — phoneMirrorManager
src/managers/phonemirrormanager.h — QML context property phoneMirrorManager
Built-in scrcpy-protocol client (see Phone Mirror): pushes tools/phone-server/octave-phone-server over adb, decodes H.264 itself and injects touch. Desktop only; the mobile build registers a stub. adb discovery (the public static findAdb()) is shared with AndroidAutoManager; on Windows every adb / server process goes through ScrcpyClient::hideConsoleWindow() so no console flashes. Errors are reported through scrcpyError only — there is no separate error signal.
CONSTANT except deviceState, deviceHint, devices and mirrorNotice, which it lacks; it omits the slots describeDeviceState, dismissNotice, refreshDeviceState, selectDevice and cleanup, and the signals audioPlayed, connectionTypeChanged, currentBitRateChanged, deviceStateChanged, mediaChanged, mediaArtChanged, mediaQueueChanged, mirrorNoticeChanged, phoneAsleepChanged, phoneInUseChanged, qrPairingChanged and wirelessBusyChanged.Properties
| Property | Type | Access | Notify signal | Description |
|---|---|---|---|---|
adbPath | QString | read-only, constant | — | adb in use (bundled platform-tools preferred) |
nativeAvailable | bool | read-only, constant | — | adb found, server jar present and libavcodec linked |
serverVersion | QString | read-only, constant | — | Phone-server version the client speaks; must match the jar |
isRunning | bool | read-only | isRunningChanged | A mirror session is active |
displaySize | QString | read-only | displaySizeChanged | Requested virtual display "WxH" or "auto" (fit the view, Android 11+); "" = phone screen |
activeDisplaySize | QString | read-only | frameSizeChanged | "WxH" of the virtual display in use, "" when mirroring the phone screen |
videoSink | QObject* | read/write | videoSinkChanged | QML binds VideoOutput.videoSink here; decoded frames go straight to it |
frameWidth | int | read-only | frameSizeChanged | Decoded frame width |
frameHeight | int | read-only | frameSizeChanged | Decoded frame height |
audioActive | bool | read-only | audioActiveChanged | Phone audio is being played through OCTAVE |
audioPlaying | bool | read-only | audioPlayingChanged | The phone is producing sound right now (drives ducking) |
phoneAsleep | bool | read-only | phoneAsleepChanged | The phone was locked / put to sleep during a session; OCTAVE is waking it |
phoneInUse | bool | read-only | phoneInUseChanged | The user unlocked the phone during a session; OCTAVE leaves its panel alone until resumeMirroring() or the phone is locked again |
wirelessAddress | QString | read-only | wirelessAddressChanged | "ip:port" of the phone last connected over Wi-Fi (setting scrcpyWirelessAddress); reconnected automatically, "" = none |
connectionType | QString | read-only | connectionTypeChanged | "usb" / "wifi" for the running session, "" when idle |
wirelessBusy | bool | read-only | wirelessBusyChanged | A QR pairing started from the UI is in progress |
qrPairingRows | QStringList | read-only | qrPairingChanged | Modules of the QR code shown by startQrPairing(), one '0'/'1' string per row; empty when no pairing is waiting |
qrPairingActive | bool | read-only | qrPairingChanged | A QR pairing is waiting for the phone |
currentBitRate | int | read-only | currentBitRateChanged | Video bit rate of the running session in bps (follows adaptive changes), 0 when idle |
mediaAvailable, mediaPlaying, mediaCanSeek | bool | read-only | mediaChanged | The phone reports a media session / it is playing / it can seek |
mediaTitle, mediaArtist, mediaAlbum, mediaApp | QString | read-only | mediaChanged | What the phone is playing; mediaApp is the package name |
mediaDuration | int | read-only | mediaChanged | Track length in ms, 0 = unknown |
mediaArtUrl | QString | read-only | mediaArtChanged | file:// URL of the phone's album art, "" for none |
mediaQueue | QVariantList | read-only | mediaQueueChanged | Phone queue entries: {id, title, artist, art_uri, art}; art is a local JPEG URL, initially empty |
mediaQueueActiveId | qint64 | read-only | mediaQueueChanged | Playing queue item ID; -1 when absent |
mediaQueueTitle | QString | read-only | mediaQueueChanged | Queue title supplied by the phone app |
sessionState | QString | read-only | sessionStateChanged | "idle" | "starting" | "streaming" | "reconnecting" (a drop after streaming: retried every second, the view holds the last frame, up to 10 s) | "waitingForPhone" (a phone / link error: retried every 2 s, restarted the moment adb reports the phone) | "failed" (setup or fatal: not retried). Retries run only while setViewActive(true) |
lastErrorKind | QString | read-only | lastErrorKindChanged | What the last scrcpyError was about: "noPhone" (no phone at all: the QR screen), "device" (the phone or its link: worth retrying), "setup" (OCTAVE's side: adb, the server, the decoder), "fatal" (the phone cannot do it: its encoder failed even in software); "" after a successful start. Decided by errorKindFor(message) next to the strings that make the messages |
deviceState | QString | read-only | deviceStateChanged | Last adb devices probe (run off the GUI thread): "device" / "unauthorized" / "no-permissions" / "offline" / "none" / "no-adb" |
deviceHint | QString | read-only | deviceStateChanged | iPhone on USB / adb server clash text, "" otherwise |
devices | QVariantList | read-only | deviceStateChanged | [{serial, state, wireless, model, selected}]; feeds the phone picker |
mirrorNotice | QString | read-only | mirrorNoticeChanged | Non-fatal notices for the running session (touch blocked, no audio, software encoder) |
mediaActive | bool | read-only | mediaActiveChanged(bool) | The phone has OCTAVE's media controls from the first session report, even when paused or no app is playing. Cleared by releaseMedia() or disconnect; after release, resumed phone playback takes control again |
Invokables & Slots
| Signature | Returns | Description |
|---|---|---|
setVolume(float volume) | void | Called by VolumeController (0–1 linear); phone audio follows OCTAVE volume |
setAudioGain(float gain) | void | Linear gain on phone PCM before OCTAVE volume (setting scrcpyAudioGain) |
setMuted(bool muted) | void | OCTAVE's mute (volumeController.mutedChanged) silences phone audio too; phone audio always plays through OCTAVE while mirroring |
setAudioDuckEnabled(bool enabled) | void | Setting scrcpyAudioDuckEnabled |
setAudioDuckLevel(float level) | void | Setting scrcpyAudioDuckLevel (linear factor) |
setAudioOutputDevice(QString id) | void | Setting audioOutputDevice: phone audio plays to the same output as local music; called again whenever the outputs change, and an open sink moves when its device would change |
setDisplaySize(QString size) | void | Virtual display size (snapped to multiples of 8, or "auto"); restarts a running session |
setViewSize(int width, int height) | void | Mirror view size in device pixels; drives "auto" and restarts a running auto session when it changes (debounced 1.5 s) |
setDisplayDpi(int dpi) | void | Setting scrcpyDisplayDpi (0 = phone's scaled density); restarts a running session |
setBitRateUsb(int mbps), setBitRateWifi(int mbps) | void | Settings scrcpyBitRateUsb / scrcpyBitRateWifi; restart a running session on that link |
setBitRateAuto(bool enabled) | void | Setting scrcpyBitRateAuto: adaptive bit rate with the per-link rates as ceiling; restarts a running session |
setAutoWifi(bool enabled) | void | Setting scrcpyAutoWifi: adb tcpip 5555 before USB sessions and remember the phone's Wi-Fi address |
setPhoneScreenOff(bool off) | void | Setting scrcpyPhoneScreenOff: keep the handset panel dark while mirroring (applied live) |
wakePhone() | void | Wake a sleeping phone (KEYCODE_WAKEUP); the overlay tap-to-retry |
resumeMirroring() | void | Leave the in-use state: wake the phone if needed and re-apply screen-off |
environmentOk() | bool | Everything OCTAVE needs is present (same as nativeAvailable); a failure is then about the phone |
getInstallInstructions() | QString | User-facing setup help text |
getDeviceState() | QString | The cached deviceState; schedules a refresh, never blocks |
refreshDeviceState() | void | Probe adb devices off the GUI thread; deviceStateChanged follows |
setViewActive(bool active) | void | The mirror page is on screen; the automatic restarts and their adb probes run only then (the page calls it from its StackView status) |
selectDevice(QString serial) | void | Mirror this phone; restarts a session running on another one |
dismissNotice() | void | Hide mirrorNotice |
describeDeviceState(QString state) | QString | Human-readable explanation of a device state |
hasConnectedDevice() | bool | The cached state is "device" |
injectTouch(int pointerId, int action, float relX, float relY) | void | Multitouch on the mirrored display: action 0 down / 1 up / 2 move; coordinates relative 0–1 |
injectKey(int keycode) | void | Send an Android keycode |
pressHome() | void | HOME; on a virtual display launches the launcher there via am start --display because HOME is routed to the default display |
pressBack() | void | BACK |
pressAppSwitch() | void | APP_SWITCH (recents) |
startScrcpy() | void | Push the server jar, start a session and begin decoding |
stopScrcpy() | void | End the session |
cleanup() | void | Stop the session at exit |
setWirelessAddress(QString address) | void | Remembered Wi-Fi phone (primed from settings at startup); tries to reach it at once |
forgetWireless() | void | Stop a Wi-Fi session, adb disconnect and clear wirelessAddress |
startQrPairing() | void | Show a QR code for the phone's “Pair device with QR code” scanner, wait up to 2 min for its mDNS pairing service, then adb pair and connect; result via wirelessResult |
cancelQrPairing() | void | Drop the shown code; the waiting job gives up within a second |
mediaPlayPause(), mediaPlay(), mediaPause(), mediaNext(), mediaPrevious() | void | Drive the phone's media session |
mediaSkipToQueueItem(qint64 id) | void | Ask the phone app to skip to an item from mediaQueue |
mediaSeek(int positionMs) | void | Seek the phone's media session |
mediaPosition() | int | Phone media position in ms, extrapolated while playing |
releaseMedia(bool pausePhone) | void | An OCTAVE source took over: the controls leave the phone, optionally pausing it |
Signals
| Signal | Description |
|---|---|
audioPlayed(QByteArray pcm, int bufferedMs) | PCM handed to the audio sink and buffered delay. Connected to AudioAnalyzer for the live waveform |
mediaQueueChanged() | Phone queue, current item, title or loaded queue artwork changed |
scrcpyStarted(int handle) | Session started (the handle is always 1 with the built-in client) |
scrcpyStopped() | Session ended |
scrcpyError(QString message) | Session failed to start / died; carries describeDeviceState()-style text for showing. lastErrorKind is set just before it |
lastErrorKindChanged() | Notify for lastErrorKind |
sessionStateChanged() | Notify for sessionState; set before the scrcpyError that caused it |
isRunningChanged() | Notify for isRunning |
displaySizeChanged() | Notify for displaySize |
frameSizeChanged(int width, int height) | Decoded size changed (also notifies activeDisplaySize, frameWidth, frameHeight) |
frameReady() | A frame reached the video sink |
videoSinkChanged() | Notify for videoSink |
audioActiveChanged(bool active) | Audio stream started / stopped |
audioPlayingChanged(bool playing) | Sound detected / silence |
phoneAsleepChanged(bool asleep) | Phone went to sleep / woke up while mirrored |
phoneInUseChanged(bool inUse) | Phone taken in hand / handed back |
mediaChanged() / mediaArtChanged() | Notify for the phone media properties (every state report) / mediaArtUrl |
mediaTrackChanged() | The track itself changed (title, artist or app), or went away on reset; nowPlaying turns it into trackChanged |
mediaActiveChanged(bool active) | The phone took / gave back the media controls; nowPlaying pauses local media when it takes over |
duckingChanged(float factor) | Duck factor while the phone makes sound and ducking is on, 1.0 otherwise; main routes it to mediaManager.setDucking |
wirelessAddressChanged(QString address) | Remembered Wi-Fi phone changed (paired, moved to a new port, forgotten); main saves it with save_scrcpy_wireless_address |
connectionTypeChanged() / wirelessBusyChanged() | Property notifications |
wirelessResult(bool ok, QString message) | Outcome of QR pairing, message ready to show |
deviceStateChanged() | A probe changed deviceState, deviceHint or devices (also emitted when a session starts, for selected) |
mirrorNoticeChanged() | mirrorNotice changed |
qrPairingChanged() | Notify for qrPairingRows / qrPairingActive |
currentBitRateChanged() | Notify for currentBitRate |
DashboardManager — dashboardManager
src/managers/dashboardmanager.h — QML context property dashboardManager
Built-in dashboard presets plus user-authored dashboards in <app data>/dashboards, with hot reload of the user dir (see Gauges & Dashboards).
Properties
| Property | Type | Access | Notify signal | Description |
|---|---|---|---|---|
dashboards | QVariantList | read-only | dashboardsChanged | Merged headers [{id, label, builtIn, path}, …], built-ins first |
lastShareError | QString | read-only | lastShareErrorChanged | Reason the last export/import failed; "" after a success |
lastError | QString | read-only | lastErrorChanged | Why the last loadDashboard / saveDashboard failed (unknown id, unreadable file, newer schema, no name, a built-in id...); "" after one that worked. The calls' return values are unchanged |
hiddenBuiltInCount | int | read-only | dashboardsChanged | Built-in presets the user deleted (hidden); shows the chooser's "Restore defaults" button |
lastImportReport | QVariantMap | read-only | lastImportReportChanged | Experimental. What the last successful import kept and left out: {id, label, cells, droppedCells, droppedProps, movedCells, portraitDropped, unknownTypes, paramIds} |
preview | QVariantMap | read-only | previewChanged | Experimental. A dashboard being edited on another device, {id, spec}, shown on the dashboard page; {} when none |
widgetPresets | QVariantList | read-only | widgetPresetsChanged | Experimental. My widgets: [{id, label, baseType, paramId, props}] from <config>/widgets/ |
templates | QVariantList | constant | — | Experimental. The starting points for a new dashboard (frontend/dashboards/templates/*.json, in their order): [{id, label, description, order, gridColumns, gridRows, cells}], each cell's data a slot {prefer, kinds} that fromTemplate() fills |
supportedSchema | int | constant | — | Highest dashboard schema this build reads (kSupportedSchema, 2: the portrait layout); DashboardRenderer shows a "needs a newer OCTAVE" message for newer specs |
Invokables & Slots
| Signature | Returns | Description |
|---|---|---|
loadDashboard(QString id) | QVariant | The spec for an id, normalised by the same rules as a save (in the grid, no overlaps, known keys; see Gauges & Dashboards); a newer-schema file untouched; empty object if unknown or unparsable |
isBuiltIn(QString id) | bool | True for a read-only preset |
saveDashboard(QVariantMap spec) | QString | Normalise and write a user dashboard JSON (needs a label; a missing id becomes uniqueIdForLabel(label), a given one must be file-safe); returns the id, "" on failure, a newer schema or collision with a built-in |
deleteDashboard(QString id) | bool | Delete a user dashboard file, or hide a built-in (persisted in .hidden-builtins) |
restoreBuiltInDashboards() | bool | Un-hide every deleted built-in; false if none were hidden |
uniqueIdForLabel(QString label) | QString | Free id for a new dashboard named label (slug, then -2, -3, ...); never a built-in id, hidden or not, or the Parameter Cards id "grid". The editor calls it before saveDashboard |
normalizedSpec(QVariantMap spec) | QVariantMap | A spec as save / load would leave it, for one that isn't a file (the editor's rescued draft); empty if unusable; an absent id stays absent |
canPlace(QVariantList cells, int gridColumns, int gridRows, int col, int row, int colSpan, int rowSpan, int exceptIndex) | bool | The rectangle is inside the grid and covers no cell but exceptIndex (-1 for none); the editor's drag / resize check |
fitAt(QVariantList cells, int gridColumns, int gridRows, int col, int row, int colSpan, int rowSpan) | QVariantMap | {col, row, colSpan, rowSpan}: the wanted span cut to the grid edge, then shrunk toward 1×1 (wider side first) until it fits; the editor's tap-to-place |
nearestFree(QVariantList cells, int gridColumns, int gridRows, int col, int row, int colSpan, int rowSpan, int exceptIndex) | QVariantMap | Experimental. {ok, col, row, colSpan, rowSpan}: the free spot of that span nearest (col, row), cell exceptIndex ignored; the editor's + Add and Duplicate |
arrange(QVariantList cells, int gridColumns, int gridRows, int index, int col, int row, int colSpan, int rowSpan) | QVariantMap | Experimental. {ok, cells, moved, swapped, reason}: cell index moved / resized there, the cells it covers swapped with it or pushed to the nearest free spot of their size; the editor's drag, resize and swap |
rescale(QVariantList cells, int fromColumns, int fromRows, int toColumns, int toRows) | QVariantMap | Experimental. {ok, cells, reason}: the layout on another grid size (edges scaled and rounded, collisions moved to the nearest free spot or a step smaller); the editor's grid density |
derivePortrait(QVariantList cells, int gridColumns, int gridRows) | QVariantMap | Experimental. {gridColumns, gridRows, cells}: a portrait layout for landscape cells, one placement each in order (the grid turned, every widget its on-screen shape on a 16:9 screen, reading order, made smaller in steps until they fit); the same for the same input |
orientedSpec(QVariantMap spec, bool portrait) | QVariantMap | Experimental. The spec as laid out on a portrait (true) screen: layouts.portrait, or one derived, as its grid and the cells' positions, without layouts; landscape returns the spec unchanged. DashboardRenderer uses it when taller than wide |
thumbnailFromImage(QVariant image) | QString | Experimental. A QImage as a 320 × 180 JPEG data URL (the editor's dashboard thumbnail); "" for anything else |
widgetPresetText(QString id) | QString | Experimental. A widget preset as shared JSON text; "" for an unknown id |
fromTemplate(QString id, QStringList supported) | QVariantMap | Experimental. A new dashboard from a template in templates (property, frontend/dashboards/templates/*.json): each cell's slot takes its first prefer id the car reports (supported; empty = no car, the first preferred), else a reported id of one of its kinds, never one already taken; no id; empty for an unknown template |
minSpan(QString type, int gridColumns, int gridRows, double cellWidth, double cellHeight, double spacing) | QVariantMap | Experimental. {colSpan, rowSpan}: the smallest span reaching the catalog's minSize for cells of that size (dp) |
sizeOptions(QString type, int gridColumns, int gridRows, double cellWidth, double cellHeight, double spacing) | QVariantList | Experimental. [{label, colSpan, rowSpan}]: S / M / L, about 1/12, 1/6 and 1/3 of the grid nearest the widget's aspect, never below minSpan; the editor's size chips |
duplicateDashboard(QString sourceId, QString newLabel) | QString | Clone any dashboard into a new user dashboard with a unique id derived from the label; returns the new id |
refresh() | void | Rescan both directories and emit dashboardsChanged |
exportDashboard(QString id) | QString | Write the spec to <Downloads>/OCTAVE-dashboards/<id>.json; returns the path, "" on failure |
copyDashboardToClipboard(QString id) | bool | Copy the spec to the clipboard as compact JSON |
importDashboard(QString fileOrUrl) | QString | Import a shared dashboard from a path, file:// or content:// URL as a new user dashboard (fresh id from the label, sanitized to schema 1); returns the new id, "" with lastShareError set on failure |
importDashboardFromClipboard() | QString | Same, from clipboard text |
importDashboardFromText(QString json) | QString | Same, from a JSON string |
previewDashboard(QVariantMap spec) | bool | Experimental. Show spec on the car's dashboard page as preview (load rules; false and no preview for an unusable one); ends 60 s after the last call |
endPreview() | void | Experimental. End the preview |
saveWidgetPreset(QVariantMap preset) | QString | Experimental. Save {label, baseType, paramId, props} (an existing id overwrites); the id, or "" with lastError |
deleteWidgetPreset(QString id) | bool | Experimental. Delete one of My widgets |
copyWidgetPresetToClipboard(QString id) | bool | Experimental. A preset as text on the clipboard |
importWidgetPresetFromText(QString json), importWidgetPresetFromClipboard() | QString | Experimental. A shared preset as a new one (never overwrites); "" with lastShareError |
Signals
| Signal | Description |
|---|---|
dashboardsChanged() | After any rescan (save, delete, duplicate, import, refresh(), or the user dir changing on disk) |
lastShareErrorChanged() | When lastShareError changes |
lastImportReportChanged(), previewChanged(), widgetPresetsChanged() | When those properties change |
FrontendManager — frontendManager
src/managers/frontendmanager.h, src/managers/frontendmanifest.h — QML context property and service object frontendManager (changes need the system permission)
Installed frontends in <app data>/frontends/<id>/, each with an octave-frontend.json, and which one is the screen from the next start (see Writing a Frontend).
Properties
| Property | Type | Access | Notify signal | Description |
|---|---|---|---|---|
frontends | QVariantList | read-only | frontendsChanged | [{id, name, version, description, kind, protocol, scopes, command, active, compatible, problem, path}]; a broken folder is listed with its problem |
activeId | QString | read-only | activeChanged | The frontend used from the next start; "" for the built-in QML UI |
runningId | QString | constant | — | What this run shows, decided at start ("" = the QML UI) |
forcedQml, forcedQmlReason | bool, QString | read-only | activeChanged | USE_QML is present (an exec frontend is not started) and its text |
lastError | QString | read-only | lastErrorChanged | Why the last install / remove / setActive failed; "" after a success |
Invokables & Slots
| Signature | Returns | Description |
|---|---|---|
installFromFolder(QString path) | QString | Copy a frontend folder in after checking it (manifest, protocol, a program for this platform, no links, at most 10000 files and 256 MB); nothing in it runs. The id, or "" with lastError |
installFromZip(QString path) | QString | Same from a .zip of the folder or of its contents; an entry naming a path outside the folder, a link or a size that doesn't match refuses the whole zip |
remove(QString id) | bool | Delete an installed frontend (not the one running) |
setActive(QString id) | bool | The screen from the next start; "" = the QML UI. Refuses one that can't run here |
clearForcedQml() | void | Delete USE_QML |
manifestOf(QString id) | QVariantMap | One row of frontends, {} if none |
Signals
| Signal | Description |
|---|---|
frontendsChanged(), activeChanged(), lastErrorChanged() | As the properties change |
installed(QString id) | An install finished |
PerfMonitor — perfMonitor
src/dev/perfmonitor.{h,cpp} — QML context property perfMonitor developer only
Event-loop / thread-jitter / CPU profiler, registered only with --profile (the class exists only in OCTAVE_DEV_TOOLS builds). Production QML never references it.
Properties
| Property | Type | Access | Notify signal | Description |
|---|---|---|---|---|
loopLatencyMs | double | read-only | metricsUpdated | Rolling average event-loop latency in ms |
loopP99Ms | double | read-only | metricsUpdated | 99th percentile loop latency (stutter indicator) |
gilJitterMs | double | read-only | metricsUpdated | Average wake-up lateness of a probe thread that sleeps 10 ms (CPU contention) |
cpuPercent | double | read-only | metricsUpdated | Process CPU usage, per core |
memMb | double | read-only | metricsUpdated | Process RSS in MB |
threadCount | int | read-only | metricsUpdated | Process thread count (Linux; read from /proc/self/status) |
summaryText | QString | read-only | metricsUpdated | One-line status for the on-screen HUD |
Invokables & Slots
| Signature | Returns | Description |
|---|---|---|
getDetailedReport() | QString | Multi-line report with loop / jitter / CPU stats and per-slot timings (p95 > 1 ms) |
Signals
| Signal | Description |
|---|---|
metricsUpdated() | Every 1 s reporting cycle (notify for every property) |