The remote control framework allows apps to control modules such as climate, radio, seat, lights, etc., within a vehicle. Newer head units can support multi-zone modules that allow customizations based on seat location.
If you are using this feature in your app, you will most likely need to request permission from the vehicle manufacturer. Not all head units support the remote control framework and only the newest head units will support multi-zone modules.
Consider the following scenarios:
Currently, the remote control feature supports these modules:
Remote Control Modules | RPC Version |
---|---|
Climate | v4.5+ |
Radio | v4.5+ |
Seat | v5.0+ |
Audio | v5.0+ |
Light | v5.0+ |
HMI Settings | v5.0+ |
The following table lists which items are in each control module.
Control Item | RPC Item Name | Value Range | Type | Comments | RPC Version Changes |
---|---|---|---|---|---|
Climate Enable | climateEnable | on, off | Get/Set/Notification | Enabled to turn on the climate system, Disabled to turn off the climate system. All other climate items need climate enabled to work. | Since v6.0 |
Current Cabin Temperature | currentTemperature | N/A | Get/Notification | Read only, value range depends on OEM | Since v4.5 |
Desired Cabin Temperature | desiredTemperature | N/A | Get/Set/Notification | Value range depends on OEM | Since v4.5 |
AC Setting | acEnable | on, off | Get/Set/Notification | Since v4.5 | |
AC MAX Setting | acMaxEnable | on, off | Get/Set/Notification | Since v4.5 | |
Air Recirculation Setting | circulateAirEnable | on, off | Get/Set/Notification | Since v4.5 | |
Auto AC Mode Setting | autoModeEnable | on, off | Get/Set/Notification | Since v4.5 | |
Defrost Zone Setting | defrostZone | front, rear, all, none | Get/Set/Notification | Since v4.5 | |
Dual Mode Setting | dualModeEnable | on, off | Get/Set/Notification | Since v4.5 | |
Fan Speed Setting | fanSpeed | 0%-100% | Get/Set/Notification | Since v4.5 | |
Ventilation Mode Setting | ventilationMode | upper, lower, both, none | Get/Set/Notification | Since v4.5 | |
Heated Steering Wheel Enabled | heatedSteeringWheelEnable | on, off | Get/Set/Notification | Since v5.0 | |
Heated Windshield Enabled | heatedWindshieldEnable | on, off | Get/Set/Notification | Since v5.0 | |
Heated Rear Window Enabled | heatedRearWindowEnable | on, off | Get/Set/Notification | Since v5.0 | |
Heated Mirrors Enabled | heatedMirrorsEnable | on, off | Get/Set/Notification | Since v5.0 |
Control Item | RPC Item Name | Value Range | Type | Comments | RPC Version Changes |
---|---|---|---|---|---|
Radio Enabled | radioEnable | true, false | Get/Set/Notification | Read only, all other radio control items need radio enabled to work | Since v4.5 |
Radio Band | band | AM, FM, XM | Get/Set/Notification | Since v4.5 | |
Radio Frequency | frequencyInteger / frequencyFraction | 0-1710, 0-9 | Get/Set/Notification | Value range depends on band | Since v4.5 |
Radio RDS Data | rdsData | RdsData struct | Get/Notification | Read only | Since v4.5 |
Available HD Channels | availableHdChannels | Array size 0-8, values 0-7 | Get/Notification | Read only | Since v6.0, replaces availableHDs |
Available HD Channels (DEPRECATED) | availableHDs | 1-7 (Deprecated in v6.0) (1-3 before v5.0) | Get/Notification | Read only | Since v4.5, updated in v5.0, deprecated in v6.0 |
Current HD Channel | hdChannel | 0-7 (1-3 before v.5.0) (1-7 between v.5.0-6.0) | Get/Set/Notification | Since v4.5, updated in v5.0, updated in v6.0 | |
Radio Signal Strength | signalStrength | 0-100% | Get/Notification | Read only | Since v4.5 |
Signal Change Threshold | signalStrengthThreshold | 0-100% | Get/Notification | Read only | Since v4.5 |
Radio State | state | Acquiring, acquired, multicast, not_found | Get/Notification | Read only | Since v4.5 |
SIS Data | sisData | SisData struct | Get/Notification | Read only | Since v5.0 |
Control Item | RPC Item Name | Value Range | Type | Comments | RPC Version Changes |
---|---|---|---|---|---|
Seat Heating Enabled | heatingEnabled | true, false | Get/Set/Notification | Indicates whether heating is enabled for a seat | Since v5.0 |
Seat Cooling Enabled | coolingEnabled | true, false | Get/Set/Notification | Indicates whether cooling is enabled for a seat | Since v5.0 |
Seat Heating level | heatingLevel | 0-100% | Get/Set/Notification | Level of the seat heating | Since v5.0 |
Seat Cooling level | coolingLevel | 0-100% | Get/Set/Notification | Level of the seat cooling | Since v5.0 |
Seat Horizontal Position | horizontalPosition | 0-100% | Get/Set/Notification | Adjust a seat forward/backward, 0 means the nearest position to the steering wheel, 100% means the furthest position from the steering wheel | Since v5.0 |
Seat Vertical Position | verticalPosition | 0-100% | Get/Set/Notification | Adjust seat height (up or down) in case there is only one actuator for seat height, 0 means the lowest position, 100% means the highest position | Since v5.0 |
Seat-Front Vertical Position | frontVerticalPosition | 0-100% | Get/Set/Notification | Adjust seat front height (in case there are two actuators for seat height), 0 means the lowest position, 100% means the highest position | Since v5.0 |
Seat-Back Vertical Position | backVerticalPosition | 0-100% | Get/Set/Notification | Adjust seat back height (in case there are two actuators for seat height), 0 means the lowest position, 100% means the highest position | Since v5.0 |
Seat Back Tilt Angle | backTiltAngle | 0-100% | Get/Set/Notification | Backrest recline, 0 means the angle that back top is nearest to the steering wheel, 100% means the angle that back top is furthest from the steering wheel | Since v5.0 |
Head Support Horizontal Position | headSupportHorizontalPosition | 0-100% | Get/Set/Notification | Adjust head support forward/backward, 0 means the nearest position to the front, 100% means the furthest position from the front | Since v5.0 |
Head Support Vertical Position | headSupportVerticalPosition | 0-100% | Get/Set/Notification | Adjust head support height (up or down), 0 means the lowest position, 100% means the highest position | Since v5.0 |
Seat Massaging Enabled | massageEnabled | true, false | Get/Set/Notification | Indicates whether massage is enabled for a seat | Since v5.0 |
Massage Mode | massageMode | MassageModeData struct | Get/Set/Notification | List of massage mode of each zone | Since v5.0 |
Massage Cushion Firmness | massageCushionFirmness | MassageCushionFirmness struct | Get/Set/Notification | List of firmness of each massage cushion | Since v5.0 |
Seat memory | memory | SeatMemoryAction struct | Get/Set/Notification | Seat memory | Since v5.0 |
Control Item | RPC Item Name | Value Range | Type | Comments | RPC Version Changes |
---|---|---|---|---|---|
Audio Volume | volume | 0%-100% | Get/Set/Notification | The audio source volume level | Since SDL v5.0 |
Audio Source | source | PrimaryAudioSource enum | Get/Set/Notification | Defines one of the available audio sources | Since SDL v5.0 |
Keep Context | keepContext | true, false | Set only | Controls whether the HMI will keep the current application context or switch to the default media UI/APP associated with the audio source | Since SDL v5.0 |
Equalizer Settings | equalizerSettings | EqualizerSettings struct | Get/Set/Notification | Defines the list of supported channels (band) and their current/desired settings on HMI | Since SDL v5.0 |
Control Item | RPC Item Name | Value Range | Type | Comments | RPC Version Changes |
---|---|---|---|---|---|
Light State | lightState | Array of LightState struct | Get/Set/Notification | Since SDL v5.0 |
Control Item | RPC Item Name | Value Range | Type | Comments | RPC Version Changes |
---|---|---|---|---|---|
Display Mode | displayMode | Day, Night, Auto | Get/Set/Notification | Current display mode of the HMI display | Since SDL v5.0 |
Distance Unit | distanceUnit | Miles, Kilometers | Get/Set/Notification | Distance Unit used in the HMI (for maps/tracking distances) | Since SDL v5.0 |
Temperature Unit | temperatureUnit | Fahrenheit, Celsius | Get/Set/Notification | Temperature Unit used in the HMI (for temperature measuring systems) | Since SDL v5.0 |
The remote control framework also allows mobile applications to send simulated button press events for the following common buttons in the vehicle.
RC Module | Control Button |
---|---|
Climate | AC |
AC MAX | |
RECIRCULATE | |
FAN UP | |
FAN DOWN | |
TEMPERATURE UP | |
TEMPERATURE DOWN | |
DEFROST | |
DEFROST REAR | |
DEFROST MAX | |
UPPER VENT | |
LOWER VENT | |
Radio | VOLUME UP |
VOLUME DOWN | |
EJECT | |
SOURCE | |
SHUFFLE | |
REPEAT |
For remote control to work, the head unit must support SDL RPC v4.4+. In addition, your app's appHMIType
must include REMOTE_CONTROL
.
Each module type can have multiple modules in RPC v6.0+. In previous versions, only one module was available for each module type. A specific module is controlled using the unique id assigned to the module. When sending remote control RPCs to a RPC v6.0+ head unit, the moduleInfo.moduleId
must be stored and provided to control the desired module. If no moduleId
is set, the HMI will use the default module of that module type. When connected to <6.0 systems, the moduleInfo
struct will be null
, and only the default module will be available for control.
Prior to using any remote control RPCs, you must check that the head unit has the remote control capability. As you will encounter head units that do not support remote control, or head units that do not give your application permission to read and write remote control data, this check is important.
This check can be performed once your SDL app has left the HMI state of NONE
. More information on how to monitor the HMI status can be found in the Understanding Permissions guide.
When connected to head units supporting RPC v6.0+, you should save this information for future use. The moduleId
contained within the moduleInfo
struct on each capability is necessary to control that module.
sdlManager.getSystemCapabilityManager().addOnSystemCapabilityListener(SystemCapabilityType.REMOTE_CONTROL, new OnSystemCapabilityListener() { @Override public void onCapabilityRetrieved(Object capability) { RemoteControlCapabilities remoteControlCapabilities = (RemoteControlCapabilities) capability; // Save the remote control capabilities } @Override public void onError(String info) { // Handle Error } });
With the saved remote control capabilities struct you can get the location of the each module and the area that it services. This will map to the grid
graphic below. This information is useful for creating a custom UI.
This data is only available when connected to SDL RPC v6.0+ systems. On previous systems, only one module per module type was available, so the module's location didn't matter. You will not be able to build a custom UI for those cases and should use a generic UI instead.
// Get the first climate module's information ClimateControlCapabilities firstClimateModule = remoteControlCapabilities.getClimateControlCapabilities().get(0); String climateModuleId = firstClimateModule.getModuleInfo().getModuleId(); Grid climateModuleLocation = firstClimateModule.getModuleInfo().getModuleLocation();
You can also get an array of seats in the SeatLocationCapability.seats
array. Each SeatLocation
object within the seats
array will have a grid
parameter. The grid
will tell you the location of that particular seat in the vehicle (See the graphic below).
sdlManager.getSystemCapabilityManager().addOnSystemCapabilityListener(SystemCapabilityType.SEAT_LOCATION, new OnSystemCapabilityListener() { @Override public void onCapabilityRetrieved(Object capability) { SeatLocationCapability seatLocationCapability = (SeatLocationCapability) capability; if (seatLocationCapability.getSeats() != null && seatLocationCapability.getSeats().size() > 0){ List<SeatLocation> seats = seatLocationCapability.getSeats(); // Save seat location capabilities } } @Override public void onError(String info) { // Handle Error } });
The grid
system starts with the front left corner of the bottom level of the vehicle being (col=0, row=0, level=0)
. For example, assuming a vehicle manufactured for sale in the United States with three seats in the backseat, (0, 0, 0)
would be the drivers' seat. The front passenger location would be at (2, 0, 0)
and the rear middle seat would be at (1, 1, 0)
. The colspan
and rowspan
properties tell you how many rows and columns that module or seat takes up. The level
property tells you how many decks the vehicle has (i.e. a double-decker bus would have 2 levels).
col=0 | col=1 | col=2 | |
---|---|---|---|
row=0 | driver's seat: {col=0, row=0, level=0, colspan=1, rowspan=1, levelspan=1} | front passenger's seat : {col=2, row=0, level=0, colspan=1, rowspan=1, levelspan=1} | |
row=1 | rear-left seat : {col=0, row=1, level=0, colspan=1, rowspan=1, levelspan=1} | rear-middle seat : {col=1, row=1, level=0, colspan=1, rowspan=1, levelspan=1} | rear-right seat : {col=2, row=1, level=0, colspan=1, rowspan=1, levelspan=1} |
Seat location does not affect the ability to get data from a module. Once you know you have permission to use the remote control feature and you have moduleId
s (when connected to RPC v6.0+ systems), you can retrieve the data for any module. The following code is an example of how to subscribe to the data of a climate module.
When connected to head units that only support RPC versions older than v6.0, there can only be one module for each module type (e.g. there can only be one climate module, light module, radio module, etc.), so you will not need to pass a moduleId
.
You can either subscribe to module data or receive it one time. If you choose to subscribe to module data you will receive continuous updates on the vehicle data you have subscribed to.
Subscribing to the OnInteriorVehicleData
notification must be done before sending the GetInteriorVehicleData
request.
sdlManager.addOnRPCNotificationListener(FunctionID.ON_INTERIOR_VEHICLE_DATA, new OnRPCNotificationListener() { @Override public void onNotified(RPCNotification notification) { OnInteriorVehicleData onInteriorVehicleData = (OnInteriorVehicleData) notification; if (onInteriorVehicleData != null){ // NOTE: If you subscribe to multiple modules, all the data will be sent here. You will have to // split it out based on `onInteriorVehicleData.getModuleData().getModuleType()` yourself. // Code } } });
After you subscribe to the InteriorVehicleDataNotification
you must also subscribe to the module you wish to receive updates for. Subscribing to a module will send a notification when that particular module is changed.
GetInteriorVehicleData getInteriorVehicleData = new GetInteriorVehicleData(ModuleType.CLIMATE) .setSubscribe(true); getInteriorVehicleData.setOnRPCResponseListener(new OnRPCResponseListener() { @Override public void onResponse(int correlationId, RPCResponse response) { // This can now be used to retrieve data <#Code#> } }); sdlManager.sendRPC(getInteriorVehicleData);
GetInteriorVehicleData getInteriorVehicleData = new GetInteriorVehicleData(ModuleType.CLIMATE) .setModuleId(moduleID) .setSubscribe(true); getInteriorVehicleData.setOnRPCResponseListener(new OnRPCResponseListener() { @Override public void onResponse(int correlationId, RPCResponse response) { // This can now be used to retrieve data // Code } }); sdlManager.sendRPC(getInteriorVehicleData);
After you subscribe to the InteriorVehicleDataNotification
you must also subscribe to the module you wish to receive updates for. Subscribing to a module will send a notification when that particular module is changed.
To get data from a module without subscribing send a GetInteriorVehicleData
request with the subscribe
flag set to false
.
GetInteriorVehicleData interiorVehicleData = new GetInteriorVehicleData(ModuleType.CLIMATE); interiorVehicleData.setOnRPCResponseListener(new OnRPCResponseListener() { @Override public void onResponse(int correlationId, RPCResponse response) { // This can now be used to retrieve data // Code } }); sdlManager.sendRPC(interiorVehicleData);
GetInteriorVehicleData interiorVehicleData = new GetInteriorVehicleData(ModuleType.CLIMATE) .setModuleId("<#ModuleID#>"); interiorVehicleData.setOnRPCResponseListener(new OnRPCResponseListener() { @Override public void onResponse(int correlationId, RPCResponse response) { // This can now be used to retrieve data // Code } }); sdlManager.sendRPC(interiorVehicleData);
Not only do you have the ability to get data from these modules, but, if you have the right permissions, you can also set module data.
Before you attempt to take control of any module, you should have your user select their seat location as this affects which modules they have permission to control. You may wish to show the user a map or list of all available seats in your app in order to ask them where they are located. See Getting Module Data Location and Service Areas for information useful in creating a custom UI showing module location and service area. The following example is only meant to show you how to access the available data and not how to build your UI/UX.
When the user selects their seat, you must send an SetGlobalProperties
RPC with the appropriate userLocation
property in order to update that user's location within the vehicle (The default seat location is Driver
).
SetGlobalProperties seatLocation = new SetGlobalProperties() .setUserLocation(selectedSeat); seatLocation.setOnRPCResponseListener(new OnRPCResponseListener() { @Override public void onResponse(int correlationId, RPCResponse response) { // Seat location updated } }); sdlManager.sendRPC(seatLocation);
Some OEMs may wish to ask the driver for consent before a user can control a module. The GetInteriorVehicleDataConsent
RPC will alert the driver in some OEM head units if the module is not free (another user has control) and allowMultipleAccess
(multiple users can access/set the data at the same time) is true
. The allowMultipleAccess
property is part of the moduleInfo
in the module object.
Check the allowed
property in the GetInteriorVehicleDataConsentResponse
to see what modules can be controlled. Note that the order of the allowed
array is 1-1 with the moduleIds
array you passed into the GetInteriorVehicleDataConsent
RPC.
You should always try to get consent before setting any module data. If consent is not granted you should not attempt to set any module's data.
GetInteriorVehicleDataConsent getInteriorVehicleDataConsent = new GetInteriorVehicleDataConsent(moduleType, moduleIDs); getInteriorVehicleDataConsent.setOnRPCResponseListener(new OnRPCResponseListener() { @Override public void onResponse(int correlationId, RPCResponse response) { GetInteriorVehicleDataConsentResponse getInteriorVehicleDataConsentResponse = (GetInteriorVehicleDataConsentResponse) response; List<Boolean> allowed = getInteriorVehicleDataConsentResponse.getAllowances(); // Allowed is an array of true or false values } }); sdlManager.sendRPC(getInteriorVehicleDataConsent);
Below is an example of setting climate control data. It is likely that you will not need to set all the data as in the code example below. When connected to RPC v6.0+ systems, you must set the moduleId
in SetInteriorVehicleData.setModuleData
. When connected to < v6.0 systems, there is only one module per module type, so you must only pass the type of the module you wish to control.
When you received module information above in Getting Remote Control Module Information on RPC v6.0+ systems, you received information on the location
and serviceArea
of the module. The permission area of a module depends on that serviceArea
. The location
of a module is like the seats
array: it maps to the grid
to tell you the physical location of a particular module. The serviceArea
maps to the grid to show how far that module's scope reaches.
For example, a radio module usually serves all passengers in the vehicle, so its service area will likely cover the entirety of the vehicle grid, while a climate module may only cover a passenger area and not the driver or the back row. If a serviceArea
is not included, it is assumed that the serviceArea
is the same as the module's location
. If neither is included, it is assumed that the serviceArea
covers the whole area of the vehicle. If a user is not sitting within the serviceArea
's grid
, they will not receive permission to control that module (attempting to set data will fail).
Temperature temp = new Temperature(TemperatureUnit.FAHRENHEIT, 74.1f); ClimateControlData climateControlData = new ClimateControlData() .setAcEnable(true) .setAcMaxEnable(true) .setAutoModeEnable(false) .setCirculateAirEnable(true) .setCurrentTemperature(temp) .setDefrostZone(DefrostZone.FRONT) .setDualModeEnable(true) .setFanSpeed(2) .setVentilationMode(VentilationMode.BOTH) .setDesiredTemperature(temp); ModuleData moduleData = new ModuleData(ModuleType.CLIMATE) .setClimateControlData(climateControlData); SetInteriorVehicleData setInteriorVehicleData = new SetInteriorVehicleData(moduleData); setInteriorVehicleData.setOnRPCResponseListener(new OnRPCResponseListener() { @Override public void onResponse(int correlationId, RPCResponse response) { // Code } }); sdlManager.sendRPC(setInteriorVehicleData);
Temperature temp = new Temperature(TemperatureUnit.FAHRENHEIT, 74.1f); ClimateControlData climateControlData = new ClimateControlData() .setAcEnable(true) .setAcMaxEnable(true) .setAutoModeEnable(false) .setCirculateAirEnable(true) .setCurrentTemperature(temp) .setDefrostZone(DefrostZone.FRONT) .setDualModeEnable(true) .setFanSpeed(2) .setVentilationMode(VentilationMode.BOTH) .setDesiredTemperature(temp); ModuleData moduleData = new ModuleData(ModuleType.CLIMATE) .setModuleId(moduleId) .setClimateControlData(climateControlData); SetInteriorVehicleData setInteriorVehicleData = new SetInteriorVehicleData(moduleData); setInteriorVehicleData.setOnRPCResponseListener(new OnRPCResponseListener() { @Override public void onResponse(int correlationId, RPCResponse response) { // Code } }); sdlManager.sendRPC(setInteriorVehicleData);
Another unique feature of remote control is the ability to send simulated button presses to the associated modules, imitating a button press on the hardware itself. Simply specify the module, the button, and the type of press you would like to simulate.
ButtonPress buttonPress = new ButtonPress(ModuleType.CLIMATE, ButtonName.EJECT, ButtonPressMode.SHORT); buttonPress.setOnRPCResponseListener(new OnRPCResponseListener() { @Override public void onResponse(int correlationId, RPCResponse response) { // Code } }); sdlManager.sendRPC(buttonPress);
ButtonPress buttonPress = new ButtonPress(ModuleType.CLIMATE, ButtonName.EJECT, ButtonPressMode.SHORT) .setModuleId("<#ModuleID#>"); buttonPress.setOnRPCResponseListener(new OnRPCResponseListener() { @Override public void onResponse(int correlationId, RPCResponse response) { // Code } }); sdlManager.sendRPC(buttonPress);
When the user no longer needs control over a module, you should release the module so other users can control it. If you do not release the module, other users who would otherwise be able to control the module may be rejected from doing so.
ReleaseInteriorVehicleDataModule releaseInteriorVehicleDataModule = new ReleaseInteriorVehicleDataModule(<#ModuleType#>) .setModuleId(moduleID); releaseInteriorVehicleDataModule.setOnRPCResponseListener(new OnRPCResponseListener() { @Override public void onResponse(int correlationId, RPCResponse response) { // Module Was Released } }); sdlManager.sendRPC(releaseInteriorVehicleDataModule);