Aircraft Systems Profiles
pkg/systems reads the user aircraft’s systems through a profile. A profile is plain data: which variables give each value. There are three layers:
- Default: the standard SimVars, for every aircraft that follows them.
- Shipped per-model profiles: go on top for aircraft that run their systems on their own variables. The first one is the Fenix A320 family, on L:vars.
- Local override files: the application’s own, on top of both. They win per value.
a := systems.Aircraft{Package: pkg.Folder, Title: title, ATCType: atcType} // addons.AircraftPackage gives the folder
p := systems.For(a, localOverrides...)
r := systems.NewReader(client, defID, reqID)
r.Use(p)
r.Request(types.SIMCONNECT_PERIOD_SECOND)
// in the message loop:
if s, ok := r.Handle(msg); ok { /* s.Battery, s.Powered, s.ExtOn, s.Squawk, … */ }
After a reconnect, call Reset. When another aircraft loads, call Use with its profile; the next Request registers the new variables.
Values
| Value | State field | Default (standard SimVar) |
|---|---|---|
battery |
Battery | ELECTRICAL MASTER BATTERY |
volts |
Volts | ELECTRICAL MAIN BUS VOLTAGE |
powered |
Powered | ELECTRICAL MAIN BUS VOLTAGE ≥ 10 V |
avionics |
Avionics | AVIONICS MASTER SWITCH |
extAvailable, extOn |
ExtAvailable, ExtOn | EXTERNAL POWER AVAILABLE:1, EXTERNAL POWER ON:1 |
com1, com2 |
COM1, COM2 (working) | COM STATUS:1/2 = 0 |
engines, engineRunning1–4, starter1–4 |
Engines, Running, Starter | NUMBER OF ENGINES, GENERAL ENG COMBUSTION:n, GENERAL ENG STARTER:n |
parkingBrake |
ParkingBrake | BRAKE PARKING INDICATOR |
lightBeacon, lightNav, lightStrobe, lightLanding, lightTaxi |
Beacon, Nav, Strobe, Landing, Taxi | LIGHT BEACON / NAV / STROBE / LANDING / TAXI |
door0–3 |
Doors | EXIT OPEN:0–3 |
xpdrState, xpdrCode |
XPDRState, Squawk (“4521”) | TRANSPONDER STATE:1, TRANSPONDER CODE:1 (Bco16) |
flapsPct, gearDown |
FlapsPct, GearDown | FLAPS HANDLE PERCENT, GEAR HANDLE POSITION |
State.Values holds every resolved value by name, including any that a profile adds.
The profile format
{
"name": "Fenix A320 family",
"match": { "packagePrefix": ["fnx-aircraft"], "titleContains": ["FNX"], "atcType": [] },
"measured": "how and where it was measured",
"values": {
"battery": { "vars": ["L:S_OH_ELEC_BAT1", "L:S_OH_ELEC_BAT2"], "combine": "any", "note": "measured" },
"volts": { "vars": ["L:N_ELEC_VOLT_BAT_1", "L:N_ELEC_VOLT_BAT_2"], "combine": "max" },
"lightStrobe": { "vars": ["L:S_OH_EXT_LT_STROBE"], "trueAt": [2] }
}
}
Frequencies. com1Active, com1Standby, com2Active and com2Standby are the COM frequencies in MHz (State.COM1Active and the others). The default reads them from COM ACTIVE/STANDBY FREQUENCY:n.
Match. A profile applies when any one rule matches:
packagePrefix: the start of the aircraft’s package folder (addons.AircraftPackage);titleContains: part of the title, any case;atcType: the ATC TYPE.
Values. Each value lists its vars in unit (default number, as L:vars are read), then:
combinejoins several vars:anyis true when any is not 0;maxandmintake the largest or smallest; nocombinetakes the first var;trueAtmakes a var true only at those positions, e.g. a three-position switch on only at 2;atLeastmakes the result true at that value or more, e.g. volts as powered;notesays whether the value was measured or assumed.
ReadProfile reads a profile from JSON and refuses a value with no vars or an unknown combine.
Order and overrides. For(aircraft, overrides...) builds the profile in three steps:
- starts from
Default(); - puts the first matching shipped profile (
Profiles()) on top; - puts each matching override on top, in the order given.
An override wins per value: values it doesn’t name stay as they were. The same applies to an override without a match that has the matched profile’s name. Merge(base, over) is that step on its own.
The Fenix A320 family
profiles/fenix-a320.json matches package folders starting with fnx-aircraft. It was measured live in MSFS 2024 at LKPR on the A319 by toggling each switch while tracing both sets of variables. The standard battery, avionics and external power values don’t follow the Fenix:
- battery and avionics read on with the aircraft dark;
- the main bus reads 28 V, battery 2’s own voltage;
- external power reads “not feeding” with EXT PWR on.
So the profile reads these from the Fenix’s L:vars:
- battery: BAT1 or BAT2;
- volts: the higher of the two battery voltages;
- powered: any AC or DC bus powered;
- avionics: AC ESS bus powered;
- external power: its AVAIL and ON lights.
Lights, parking brake, EXIT OPEN:0 and the transponder follow the standard variables and stay as they are. Live, the two profiles disagreed only where the Fenix differs: external power on, and the volts.
Radios. These come from the RMPs, measured powered and dark:
- COM working:
L:B_PED_RMP1_POWERandRMP2_POWER. - The frequencies:
L:N_PED_RMP1_ACTIVEand_STDBY, in kHz scaled to MHz (scale: 0.001).COM STANDBY FREQUENCY:1does not follow the RMP. - RMP 2 is assumed to work as RMP 1, and marked so.
The profile’s actions give the COM swap as the RMP transfer key, L:S_PED_RMP1_XFER (see Radios and Transponder).
Actions
actions names how a control is operated on a model where the standard key events do not do it: {"com1Swap": {"press": "L:S_PED_RMP1_XFER"}} presses that variable (1, then 0). pkg/avionics takes them with Radios.Use(profile.Actions). They merge like values: an override wins per action.
An action is one of: press (a button variable clicked), set (a variable set to the state wanted, 1 or 0), event (a key event; with toggle sent only when the state differs, with data for its parameter), or efb (a boolean data ref written through the aircraft’s tablet API, Profile.EFB; Controls.SetEFBHost for an app on another machine).
Ground controls
Controls operates the user aircraft’s ground controls by name, the same way for every aircraft (#667): Door(n), Chocks, GPU, ParkingBrake. The profile says how:
- Default: the exits by
TOGGLE_AIRCRAFT_EXITwith their index from 1, toggled only when not as wanted; the parking brake byPARKING_BRAKES, likewise. No chocks or GPU. - Fenix A320 family (measured live on the Fenix A319, 2026-10-04): the main door and the parking brake by the default key events; chocks and its GPU through its EFB API (
efbactions:fenix.efb.chocks,groundservice.groundpower, GraphQLwriteBoolon port 8083, as its EFB does; a write toL:B_CONFIG_CHOCKSorL:B_CONFIG_GPUdoes not stick); read back from those L:vars. With the chocks off the Fenix reads its parking brake released.
ctl := systems.NewControls(client, 0)
ctl.Use(profile) // systems.For(the aircraft)
if ctl.Can(systems.Chocks) {
ctl.Set(systems.Chocks, false, reader.State()) // remove them
}
ctl.Set(systems.Door(0), true, reader.State()) // open the main door
State reads Chocks and GPU, with HasChocks and HasGPU when the model has them. Can tells the app which buttons to show.
Ground services
The sim’s own ground services for the user aircraft are requested by name with Controls.Request (#666): Jetway (TOGGLE_JETWAY, at a parking spot; asked again, sent away), Stairs (TOGGLE_RAMPTRUCK), Baggage (REQUEST_LUGGAGE), Catering (REQUEST_CATERING), PowerSupply (REQUEST_POWER_SUPPLY), FuelTruck (REQUEST_FUEL_KEY, at a parking spot) and Pushback (TOGGLE_PUSHBACK): the standard key events (MSFS 2024 SDK Key Events) by default, a model’s own way where its profile gives one. State reads the pushback: PushbackAttached, PushbackAvailable, PushbackWait (Services Variables). An app that drives GSX uses it instead where GSX runs.
EFB
A profile’s efb is where the aircraft serves its tablet over HTTP: the Fenix’s EFB on port 8083 ({"port": 8083, "path": "/"}, plain HTTP, all interfaces); none in the default.