State, sources, and authority
Radio owns vehicle eligibility, permissions, synchronized source and station selection, power, volume intent, timestamps, metadata, Bluetooth transport, scanner power, and the head-unit interfaces. The selected provider only renders that state.
| Source | Media owner | Synchronization model |
|---|---|---|
| Local Radio | GTA V | Native station, power, and track metadata are synchronized; GTA owns playback gain. |
| Satellite Radio | External stream operator | Vehicle state is synchronized; matching live URLs can share a provider syncGroup. |
| Server Radio | Configured Station Pack | One deterministic program clock supplies a seekable track and offset. |
| Bluetooth | Player-supplied supported URL | Vehicle queue, URL, transport position, play state, and revision are synchronized. |
| Scanner | Local Dispatch or configured live profile | Separate vehicle channel, power state, public metadata, and HUD. |
The server validates the real requesting player, networked vehicle, seat permission, vehicle eligibility, station or URL, source, and rate limit before changing shared state. NUI state is never an authority boundary.
New vehicle state is powered off by default. Normal entertainment restrictions and scanner eligibility are independent so emergency controls can remain available while the entertainment UI is blocked.
Requirements and installation
- Keep the exact san_andreas_radio folder name.
- OneSync is required and declared by dependency '/onesync'.
- Radio has no framework, database, npm-install, or build requirement.
- Start the selected audio provider before Radio. Start optional Dispatch and Station Packs before Radio when their sources are used.
- Without Dispatch, only scanner profiles with a non-empty URL can play. Without Station Pack, Local, Satellite, and Bluetooth remain available.
ensure san_andreas_sound
ensure san_andreas_dispatch
ensure san_andreas_station_pack
ensure san_andreas_radio- Confirm OneSync, start order, and a clean server console.
- Enter a normal vehicle, start the engine, and open the head unit with Q or /saradio.
- Confirm the radio begins powered off, then test every installed source.
- Confirm the mini display follows radio, engine/accessory, and pause state.
- Enter a restricted emergency vehicle and confirm the entertainment UI remains blocked.
- Enter an eligible scanner vehicle and verify its scanner power and HUD separately.
Core state, permissions, and lifecycle
| Key | Type | Default | Accepted values | Description |
|---|---|---|---|---|
Config.OpenKey | string | Q | — | Default player-remappable head-unit key. |
Config.HoldToOpen | boolean | false | — | Uses hold behavior when true and toggle/Escape behavior when false. |
Config.DisableNativeWheel | boolean | true | — | Suppresses GTA's competing radio wheel while eligible. |
Config.DisableNativeCycling | boolean | true | — | Suppresses GTA next/previous station controls. |
Config.PassengerControl | string | all | driver | front | all | Seats allowed to change shared radio state. |
Config.DefaultStation | string | RADIO_01_CLASS_ROCK | — | Initial native station for new vehicle state. |
Config.DefaultVolume | number | 0.20 | 0.0-1.0 | Initial synchronized volume intent. |
Config.PoweredByDefault | boolean | false | — | Power state for new empty or player-driven vehicles. |
Config.PreserveNpcRadio | boolean | true | — | Preserves an NPC driver's active GTA station once during takeover. |
Config.MinVolume / MaxVolume | number | 0.0 / 1.0 | — | Slider bounds before managed output scaling. |
Config.ServerRateLimitMs | number | 75 | — | Minimum interval between accepted state-changing requests. |
| Key | Type | Default | Description |
|---|---|---|---|
VehicleRestrictions.DisableEmergencyVehicles | boolean | true | Blocks the normal entertainment UI for GTA class 18. |
VehicleRestrictions.DisabledModels | array | {} | Spawn names or model hashes that cannot use normal Radio. |
AccessoryPower.Enabled | boolean | true | Retains a powered radio after engine shutdown. |
AccessoryPower.ResumeOnEngineStart | boolean | true | Restores remembered source, station, volume, and playlist state. |
AccessoryPower.DriverDoorIndex | number | 0 | Door that releases retained accessory power. |
AccessoryPower.DoorOpenThreshold | number | 0.05 | Open ratio that triggers retained-power shutdown. |
engine running + radio on
-> engine off: accessory state remains active
-> driver door opens: radio powers down and remembers state
-> later engine start: remembered source and station restoreBluetooth resumes from its retained position only when it had been playing before shutdown. The engine-stop and entity-resolution grace periods prevent brief native-state or network-entity flicker from destroying and recreating media.
Displays and controls
| Key | Type | Default | Description |
|---|---|---|---|
MiniRadio.EnabledByDefault | boolean | true | Default local compact-display preference. |
MiniRadio.X / Y | number | 0.88 / 0.84 | Default normalized screen position. |
MiniRadio.Scale | number | 1.0 | Default player scale. |
MiniRadio.MinScale / MaxScale | number | 0.70 / 1.50 | Player resize bounds. |
AccentPicker.Enabled | boolean | true | Lets players save a fixed display accent or choose AUTO. |
AccentPicker.DefaultColor | hex color | #41d9ff | Fallback accent for the display. |
ScannerHud.VisibleByDefault | boolean | true | Initial local HUD preference. |
ScannerHud.ToggleCommand / ToggleKey | string | sascanner / J | Synchronized scanner power controls. |
ScannerHud.VisibilityCommand | string | scannerhud | Local HUD-only visibility control. |
ScannerHud.DriverOnly | boolean | true | Limits scanner HUD presentation to the driver. |
ScannerHud.SpectrumBands | number | 24 | Display band count; direct Sound sources can supply real spectrum data. |
ScannerHud.ActivityThreshold / ActivityHoldMs | number | 0.045 / 550 | Voice-style meter trigger and hold behavior. |
| Command | Arguments | Context | Description |
|---|---|---|---|
/saradio | — | Client | Toggle the main head unit. |
+sanandreasradio / -sanandreasradio | Q by default | Client | Mapped head-unit press and release commands. |
Escape | — | Client | Close the focused head unit without opening the native pause menu. |
/saradiotrack | — | Client | Print native station and track metadata to F8 when configured. |
/sascanner | — | Client | Toggle synchronized scanner power. |
/scannerhud | — | Client | Toggle only this player's scanner HUD preference. |
The mini-radio position, scale, visibility, and accent are local KVP preferences. It hides while paused, powered off, or before engine/accessory state permits it.
Scanner power is shared per vehicle; scanner HUD visibility is local. When compatible spectrum data is unavailable, the HUD uses a procedural voice-style activity meter.
Audio provider and managed output
| Key | Type | Default | Description |
|---|---|---|---|
Config.Audio.Enabled | boolean | true | Master switch for Satellite, Server Radio, Bluetooth, and scanner rendering. |
Config.Audio.OutputVolumeScale | number | 0.30 | Maps the full head-unit slider onto a safer provider range. |
Config.Audio.OutputVolumeCeiling | number | 0.82 | Soft upper guard for music and Bluetooth. |
Config.Audio.ScannerStartupFadeMs | number | 180 | Starts Dispatch muted, seeks, then fades to profile volume. |
Config.Audio.SyncSatelliteStreams | boolean | true | Supplies stable syncGroup hints for matching live stations. |
Config.Audio.Provider | string | san_andreas_sound | Selected adapter name. |
Config.Audio.Resources | map | — | Maps adapter names to installed resource folders. |
Config.Audio.Resource | string | san_andreas_sound | Legacy/custom provider folder override. |
Config.Audio.MaxDistance | number | 28.0 | Normal managed-music radius. |
Config.Audio.ExteriorVolumeMultiplier | number | 0.18 | Exterior music trim. |
Config.Audio.ExteriorVolumeMultiplierByClass[14] | number | 0.30 | Higher trim for generally open-cabin boats. |
Config.Audio.ExteriorMaxDistance | number | 18.0 | Exterior managed-music radius. |
Config.Audio.EngineStopGraceMs | number | 1200 | Avoids churn during engine-state flicker. |
Config.Audio.EntityResolveGraceMs | number | 1500 | Retains audio through brief network-entity lookup gaps. |
Config.Audio.PositionUpdateMs | number | 350 | Compatibility provider position update interval. |
Local GTA Radio
Config.Stations controls which valid native GTA station IDs appear and in what tuner order. Removing an entry hides it from this UI. Radio reads GTA's active track text ID, resolves built-in labels, validates the driver as metadata publisher, and shares the result with occupants.
Optional artwork is a local JPG under san_andreas_radio/web/assets/stations. Without an explicit filename, the NUI derives a lowercase label slug. Add manifest patterns before using other artwork extensions.
| Key | Type | Default | Description |
|---|---|---|---|
Config.Metadata.Enabled | boolean | true | Publishes native track metadata. |
Config.Metadata.PollMs | number | 750 | Client native-track polling interval. |
Config.Metadata.RetuneDelayMs | number | 1000 | Delay before accepting metadata after tuning. |
Config.Metadata.ServerRateLimitMs | number | 400 | Minimum server interval for validated metadata updates. |
Config.Metadata.DebugCommand | string | saradiotrack | F8 diagnostic command. |
Config.TrackMetadata[id] | table | — | Optional title/artist correction for missing GTA labels. |
Satellite Radio and now-playing metadata
- Confirm permission for the intended server use and distribution of configuration and artwork.
- Find a direct browser-playable endpoint rather than a web-player page. Prefer stable HTTPS MP3, AAC, OGG, or browser-playable HLS without login, cookies, or expiring tokens.
- Test the exact endpoint in FiveM's embedded browser environment.
- Prepare locally hosted, authorized station artwork and add the station to the active Config.Streams table.
- Add JSON or SSE metadata mapping when available, or provide Config.StreamMetadata fallback text.
- Restart Radio, test inside/outside behavior and cleanup, and compare two vehicles on the same live station.
| Key | Type | Default | Required | Description |
|---|---|---|---|---|
id | string | — | Yes | Unique synchronized identity; an uppercase SATELLITE_ prefix is recommended. |
label | string | — | — | Full station name. |
short | string | SAT | — | Compact source label. |
genre | string | — | — | Description beneath now-playing text. |
accent | #RRGGBB | — | — | Station display color. |
artwork | string | — | — | Radio-owned JPG filename under web/assets/stations. |
url | URL | — | Yes | Direct media endpoint. |
metadata | table | — | — | Optional JSON polling or SSE definition. |
| Key | Type | Default | Accepted values | Required | Description |
|---|---|---|---|---|---|
type | string | — | json | sse | Yes | Transport used for now-playing data. |
url | HTTPS URL | — | — | Yes | CORS-enabled metadata endpoint. |
pollMs | number | — | — | — | Clamped JSON polling interval; do not use it as high-frequency telemetry. |
dataPath | dot path | — | — | — | Selects the record or list containing current data. |
station / stationPath | string / dot path | — | — | — | Selects one station from a shared feed. |
artistPath / titlePath / idPath | dot path | — | — | — | Maps separate artist, title, and stable update ID fields. |
combinedPath / separator | dot path / string | — | — | — | Splits one combined Artist - Title field. |
fallbackArtistPath / fallbackTitlePath | dot path | — | — | — | Program-level fallback fields when song values are absent. |
The stream plays but fallback text never updates.
Likely causes
- Metadata CORS is blocked
- The response path is wrong
- The endpoint is not HTTPS
Checks
- Inspect the exact response
- Correct data/artist/title paths
- Test endpoint CORS in FiveM CEF
Expected result
Validated now-playing fields synchronize to occupants.
The feed selects the wrong station record.
Likely causes
- station and stationPath are missing or incorrect
Checks
- Map the shared feed's station key
Expected result
The configured station record is selected before metadata paths are read.
Two vehicles drift on one live station.
Likely causes
- SyncSatelliteStreams is disabled
- The provider ignores syncGroup
- CORS forced HTML fallback
Checks
- Confirm matching URL/group and webaudio diagnostics
Expected result
A compatible provider fans one live decode into independent vehicle branches.
Scanner profiles and Dispatch mapping
| Desired source | Dispatch mapping | Profile URL |
|---|---|---|
| Local Dispatch only | Map profile to channel | May remain empty. |
| External stream only | Leave unmapped or disable Dispatch | Required direct URL. |
| Local Dispatch with fallback | Map profile to channel | Required direct fallback URL. |
| YouTube live fallback | Either mapped or unmapped | Supported channel /live URL with resolver enabled. |
| Key | Type | Default | Required | Description |
|---|---|---|---|---|
Id | string | — | Yes | Stable lowercase profile ID used by power and Dispatch mapping. |
Enabled | boolean | true | — | Profile master switch. |
Url | URL | — | — | External source or fallback; may be empty for a mapped local channel. |
ResolveYouTubeLive | boolean | false | — | Resolve a supported YouTube channel /live page. |
YouTubeRefreshMinutes | number | 10 | — | Live-page refresh interval. |
Label / HudLabel | string | — | Yes | Full channel name and compact receiver heading. |
HudAccent | #RRGGBB | — | Yes | Scanner display color. |
Volume | number | 0.10 | — | In-cabin scanner source level. |
RequireEngine | boolean | false | — | Require an active engine. |
DisableNativeScanner | boolean | true | — | Suppress GTA scanner for this profile. |
Loop | boolean | true | — | Loop continuous external profiles; local Dispatch descriptors remain non-looping. |
VehicleModels | array | — | — | Explicit spawn names checked before broad classes. |
VehicleClasses | array | — | — | Broad GTA class eligibility. |
DisabledModels | array | — | — | Hard exclusions applied before profile matching. |
MaxDistance | number | 20.0 | — | Scanner radius. |
MuffleOutsideVehicle | boolean | true | — | Apply provider exterior shaping. |
ExteriorVolumeMultiplier | number | 0.30 | — | Exterior scanner trim; marine defaults to 0.50. |
ExteriorMaxDistance | number | 10.0 | — | Exterior scanner radius. |
EngineStopGraceMs | number | 1200 | — | Avoid brief lifecycle churn. |
| GTA class or strategy | Typical profile |
|---|---|
| Explicit model before broad class | Fire apparatus and ambulances before the class 18 emergency fallback. |
| 18 | Police/general emergency fallback. |
| 15 and 16 | Aviation. |
| 14 | Marine. |
| 17 and 20 plus explicit utility models | Service/commercial. |
Profiles are evaluated in order. Explicit VehicleModels win before broad VehicleClasses, and a model listed in any enabled profile's DisabledModels receives no configured scanner.
For local Dispatch, Config.Dispatch.ProfileChannels maps Radio profile IDs to Dispatch channel IDs. A healthy local descriptor takes priority; a profile URL is used only when the profile is unmapped or its mapped source is unavailable.
- Restart Dispatch after catalog changes and Radio after profile or mapping changes.
- Enter an explicitly listed model and confirm it wins over a broad class profile.
- Verify channel, system, talkgroup, destination, frequency, and STANDBY/RECEIVING state.
- Toggle scanner power with the key, /sascanner, and conditional head-unit control; use /scannerhud to test local visibility only.
- Confirm GTA's native scanner is suppressed only where intended.
- Stop Dispatch temporarily only when intentionally testing a configured URL fallback.
- Test engine state, exit/re-entry, pause, deletion, and independent restarts.
Register Station Packs
| Key | Type | Default | Description |
|---|---|---|---|
Config.ServerRadio.Enabled | boolean | true | Makes Server Radio available when a valid pack is running. |
Config.ServerRadio.AcousticClass | string | server_music | Optional provider hint for peak-controlled local media. |
Config.ServerRadio.VolumeMultiplier | number | 0.70 | Inside-and-outside trim for locally hosted station media. |
Config.ServerRadio.ExteriorVolumeMultiplier | number | 0.75 | Additional exterior trim after normal Radio exterior shaping. |
Config.ServerRadio.Resources | array | { 'san_andreas_station_pack' } | Exact installed station-pack resource names. |
- Install each pack beside Radio and start it before Radio.
- Append each exact folder name to Config.ServerRadio.Resources; do not overwrite other needed packs.
- Confirm every pack exposes GetStations and GetSnapshot and emits the local stationPackChanged event.
- Restart the pack and then Radio, choose the SERVER source, and verify catalog, artwork, metadata, and audio.
- Tune two vehicles at different times and confirm the same program offset; restart only the pack and confirm its stable clock reconstructs the schedule.
- Normalize local media before adjusting the two Server Radio trims.
Bluetooth playlists and URL security
| Key | Type | Default | Accepted values | Description |
|---|---|---|---|---|
Config.Bluetooth.Enabled | boolean | true | — | Enables player-supported URL playback. |
FetchVideoTitles | boolean | true | — | Uses YouTube oEmbed to resolve presentation titles. |
AllowDirectUrls | boolean | false | — | When false, restricts URLs to the configured host allowlist. |
AllowHttp | boolean | false | — | Rejects insecure HTTP by default. |
MaxUrlLength | number | 512 | — | Maximum accepted submitted URL length. |
AllowedHosts | array | — | — | Approved YouTube and youtube-nocookie hosts in the release config. |
Playlist.Enabled | boolean | true | — | Enables saved multi-link lists. |
Playlist.MaxTracks | number | 30 | — | Player saved-list cap; Radio's effective safety maximum is 100. |
Playlist.AllowDuplicates | boolean | false | — | Controls repeated URLs. |
Playlist.AutoAdvance | boolean | true | — | Advances after provider-reported natural completion. |
Playlist.RepeatMode | string | all | off | all | one | Default repeat behavior. |
Playlist.ShuffleByDefault | boolean | false | — | Default local shuffle preference. |
The full saved playlist remains in the player's local KVP. The server receives a validated transient queue for the active vehicle; other occupants receive only current media and the queue summary needed for synchronization.
The server rejects credentials, whitespace, control characters, unsupported schemes, overlong values, and unapproved hosts. Client or NUI validation is only feedback; the server revalidates every request and the real vehicle/seat authority.
Natural completion advances the queue once. A changing Bluetooth revision rejects duplicate completion reports from multiple listeners; intentional destroy or replacement must not invoke auto-advance.
PMA live-player scanner bridge
The optional bridge rebroadcasts selected pma-voice radio channels into powered vehicle scanners without joining listeners to the talker's channel or modifying their PMA radio/call state.
| Key | Type | Default | Accepted values | Required | Description |
|---|---|---|---|---|---|
LiveVoiceScanner.Enabled | boolean | false | — | — | Leave disabled until mappings and privacy behavior pass a multiplayer test. |
Provider / Resource | string | pma_voice / pma-voice | — | — | Selected internal voice adapter and resource folder. |
VoiceTarget | number | 1 | 1-32 | — | PMA receive target used by the adapter. |
Volume | number | 0.22 | — | — | Scanner voice level. |
UseSubmix | boolean | true | — | — | Requests the configured PMA voice treatment. |
NaturalVoiceDistance | number | 4.0 | — | — | Avoids duplicate scanner routing when the talker is naturally audible. |
DispatchDuckMultiplier | number | 0.20 | — | — | Lowers scheduled Dispatch while live voice is active. |
MaxTransmissionMs | number | 120000 | — | — | Releases a stuck transmission. |
ShowPlayerName | boolean | false | — | — | Privacy-preserving default; true sends the current server name to eligible listeners. |
ProfileChannels | map | — | — | Yes | Maps scanner profile IDs to positive PMA channel definitions with labels, frequency, and priority. |
- Start pma-voice before Radio when the bridge is enabled.
- A talker must actively transmit on a configured PMA channel; a listener must occupy a powered matching scanner vehicle.
- Radio avoids a duplicate path when listener and talker share the PMA radio channel, share a call, or are within NaturalVoiceDistance.
- One talker is selected per vehicle. Higher profile priority wins; optional Range can constrain reach. Keep channel numbers unique because the first duplicate definition wins.
- Set UseSubmix=false temporarily when isolating provider or submix behavior.
- Put client A on one mapped PMA radio channel.
- Put client B in an eligible powered scanner vehicle, on a different PMA channel or none, and move beyond natural voice range.
- Transmit from A and confirm B hears the scanner route and sees the configured metadata.
- Move B onto A's channel, then within natural range, and confirm no obvious duplicate path.
- Test Dispatch ducking, scanner off, exit, deletion, talker disconnect, timeout, and independent resource restarts.
- Test player-name display both ways, then restore the intended privacy setting.
Public cross-resource contract
GetVehicleRadioState
exportServerReturn the suite-managed raw state for one networked vehicle. Copy needed fields immediately and never mutate or retain the returned table.
GetVehicleRadioState(vehicleNetId)- Returns
- nil or raw read-only state table
- State includes station, mode, volume, powered, accessory, spatial, track, Bluetooth, and revision.
- Radio exposes no supported external setter, callback, or outward state-change event.
- Internal san_andreas_radio network events are validated player-action implementation details, not public automation APIs.
| State area | Fields |
|---|---|
| Core | station, mode, volume, powered, accessory, spatial, revision |
| Track | false or { id, title, artist, source } |
| Bluetooth | url, label, playing, base position, startedAt, and revision |
Diagnostics and troubleshooting
The head unit will not open.
Likely causes
- OneSync or lifecycle state
- Vehicle restriction
- Seat permission
- Key mapping
Checks
- Confirm current vehicle/seat
- Check engine/accessory state and restrictions
- Try /saradio
Expected result
An eligible occupant opens the UI and the server accepts validated state requests.
A Satellite station is silent.
Likely causes
- Not a direct media URL
- HTTPS/codec/provider failure
- Browser embedding or CORS behavior
Checks
- Test the exact endpoint
- Inspect F8 and Sound diagnostics
Expected result
The provider confirms playback through webaudio, html, or documented YouTube fallback.
A scanner never appears or uses the wrong source.
Likely causes
- Profile order or disabled model
- Missing mapping with blank URL
- Class/model spelling
- Dispatch unavailable
Checks
- Inspect selected profile and mapping
- Test explicit model precedence
- Check channel state and fallback URL
Expected result
The intended eligible profile supplies local Dispatch or its configured external source.
GTA and replacement scanners both play.
Likely causes
- DisableNativeScanner is false on the selected profile
Checks
- Correct the matching profile, not a different profile
Expected result
Native chatter is suppressed only for intended replacement profiles.
Server Radio starts from zero or is too loud.
Likely causes
- Incorrect final duration or seek path
- Hot local mastering
- Trims too high
Checks
- Verify Station Pack snapshot and duration
- Inspect Sound start confirmation
- Normalize media and lower both Server Radio trims
Expected result
Late listeners seek to the current offset at a balanced level.
Bluetooth stops after one track.
Likely causes
- AutoAdvance disabled
- Provider does not report natural completion
- Intentional replacement incorrectly calls onPlayEnd
Checks
- Inspect Playlist.AutoAdvance and provider callback semantics
Expected result
Exactly one natural completion advances the queue.
Audio duplicates after restart.
Likely causes
- More than one provider instance
- Provider or caller cleanup does not own names correctly
Checks
- Run one selected provider
- Restart each resource separately and inspect remaining diagnostics
Expected result
Resource restart removes only owned playback and leaves no duplicate.
- Complete the full control, source, tuner, playlist, permission, lifecycle, UI, and scanner pass with at least two clients and two vehicles.
- Test same and different Satellite stations, every installed Dispatch profile, Station Pack late joining, and PMA routing when enabled.
- Restart Sound, Dispatch, Station Pack, and Radio independently and confirm unrelated vehicle resources continue running.