Webportal v2

embedded apps

Application - Quickstart

Building an Application for WebPortal is made easy using web technologies, indeed Apps runs in an iframe in the embedded browser!

  • πŸ‘¨πŸ½β€πŸ’» Build user interface using HTML, CSS and JS.
  • πŸš— Access vehicle APIs.
  • 🌏 MQTT is available as message queuing service.

Head Unit version: remember that they are 2 different version of the vehicle head unit, they are not providing the same vehicle APIs. However this tutorial explains how to build hybrid Apps.

  • NAC were produced between 2017 and 2021, supports ES5 & CSS2.
  • IVI are produced since mid-2021, supports ES6 & CSS3.

Don’t work alone! #

You will need to contact Stellantis Team in order to develop and deploy your Apps on WebPortal, so you should start by doing so! App need to be validated before publication. The team will be able to help you with questions you might have ;)

App Structure & Package #

This tutorial will guide you through the creation of your Application. You can also download this demo App as an example.

This is the file structure your application should use:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
app_<appId>-v<X.Y.Z>_<YYYYMMDD>.md5
app_<appId>-vX.Y.Z_YYYYMMDD.tar.gz
β”‚
└───web/
    └───apps/
    β”‚   └───<appId>/
    β”‚       β”‚   info.json
    β”‚       β”‚   index.html (webportal is a single page app)
    β”‚       β”‚   main.js
    β”‚       β”‚   main.css
    β”‚       β”‚   
    β”‚       └───js/
    β”‚       β”‚      other.js
    β”‚       β”‚
    β”‚       └───css/
    β”‚       β”‚      other.css
    β”‚       β”‚
    β”‚       └───icons/
    β”‚       β”‚      icon-<brand>-100x100.png  (v1 SD screens)
    β”‚       β”‚      icon-<brand>-136x136.png  (v1 HD screens)
    β”‚       β”‚      icon-ds-160x160.png       (v2 DS only)
    β”‚       β”‚      icon-citroen-190x190.png  (v2 CitroΓ«n only)
    β”‚       β”‚      icon-<brand>-128x128.png  (v2 all other brands)
    β”‚       β”‚
    β”‚       └───img/
    β”‚              asset.png
    β”‚
    └───media/
        └───<appId>/
               audio.mp3
               art.jpg

In order to deliver an App, please submit the following package to Stellantis for us to check its behavior before actually deploying it, see App validation. The file structure should follow these requirements:

  • package: The files must be sent in a TAR archive compressed via GZIP, check out the naming section.
  • md5: The .md5 file is required in order to perform an integrity check, see md5 file requirements
  • info.json: application manifest.
  • index.html: file must be present at the root of the project and is the starting point of the application, webportal application are single page app!
  • css: css main file should be located at the root of the project, other css files are located in the css folder. In WebPortal v1 CSS2 is supported, in WebPortal v2 CSS3 is supported.
  • javascript js main file should be located at the root of the project, other javascript files are located in the js folder. In WebPortal v1 ES5 is supported, in WebPortal v2 ES6 is supported.
  • icons: they must be present in the different screen sizes, check out the manifest.
  • img: folder for image files, if your App is storing a lot of offline data, you should use the media folder.
  • media: this folder is required only for App storing a large quantity of static media files, otherwise, media can be stored in web/apps/<appId>.
  • adding folders: the previous file structure cannot be modified but it is possible to add folders if necessary.

App loading: Optimize your code and consider minification, heavy App means long downloading time.

App manifest

The Application package should contain a manifest info.json:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
  "artifactId": "<appid>",
  "name": "<appname (optional)>",
  "description": "<appdescription (optional)>",
  "version": "X.Y.Z",
  "buildDate": "YYYY-MM-DD HH:mm",
  "icons": {
    /* Brand specific icon (optionnal)
     "<brand-code>": {
       "100": "icon/icon-<brand>-100x100.png", 
       "136": "icon/icon-<brand>-136x136.png", 
       "160": "icon/icon-<brand>-160x160.png", 
       "190": "icon/icon-<brand>-190x190.png", 
       "128": "icon/icon-<brand>-128x128.png", 
    }, */
    // Default icon
    "*": {
      "100": "icon/icon-100x100.png", // (v1 SD screens)
      "136": "icon/icon-136x136.png", // (v1 HD screens)
      "160": "icon/icon-160x160.png", // (v2 DS only)
      "190": "icon/icon-190x190.png", // (v2 CitroΓ«n only)
      "128": "icon/icon-128x128.png", // (v2 all other brands)
    },
}

Where <brand-code>:

  • Peugeot: AP
  • Citroen: AC
  • DS: DS
  • Opel: OP
  • Vauxhall: VX

Naming and md5 file

  • File name:
    • package filename: app_<appId>-v<X.Y.Z>_<YYYYMMDD>.tar.gz
    • integrity filename: app_<appId>-v<X.Y.Z>_<YYYYMMDD>.md5
  • File content:
    • must be in the following format:
      <MD5_hash_lowercase> app_<appId>-v<X.Y.Z>_<YYYYMMDD>.tar.gz
    • must be newline terminated: \n
Example

For a package:

  • in version 1.2.6,
  • released on the 17th January 2022,
  • with appId is demoApp

Package name will be: app_demoApp_v1.2.6_20220117.tar.gz, and MD5 file content could be:

1
2
098f6bcd4621d373cade4e832627b4f6 app_demoApp_v1.2.6_20220117.tar.gz

HTML (index.html) #

The HTML part of your App is located in the index.html file.

Webportal Apps being Single Page Application, this file is the only html file in the project, you should not use hyperlinks to browse another html file in your App. In order to make the content of your App dynamic, use javascript.

jQuery v1.10.0 library is used for webportal purpose and available offline for App development.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<html>
    <meta charset="UTF-8">
    <head>
      <!-- STYLES -->
      <link rel="stylesheet" type="text/css" href="css/main.css">
      <!-- SCRIPTS -->
      <script type="text/javascript" src="/shared/libs/jquery/1.10.0/jquery.js"></script>
      <script type="text/javascript" src="js/main.js"></script>
    </head>
    <body>
      <div id="my-app">
          Hello World!
      </div>
    </body>
</html>

CSS (main.css) #

CSS file should manage your App style. Using media query it’s possible to target a specific head unit platform.

CSS Version: Webportal v1 supports CSS2 which does not include features like flex box. WebPortal v2 supports CSS3 but you might want to stick to CSS2 if you are building a hybrid Application.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* All devices */
#my-app {
    color: blue;
}

/* Only for Webportal v1 SD*/
@media(max-width:1000px){
}

/* Only for Webportal v1 HD*/
@media(min-width:1000px) and (max-width:1500px){
}

/* Only for Webportal v2*/
@media(min-width:1500px){
}

JavaScript (main.js) #

Here is the minimum JavaScript code for your application to function properly.

JS Version: Webportal v1 only supports ES5, WebPortal v2 supports ES6. Check-out demo app for an example of hybrid application.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// You should execute javascript only when the DOM is ready
document.addEventListener("DOMContentLoaded", function(event) {

  (async () => {
    // Initializing the SDK is required
    // in order to use vehicles APIs
    const {default : IDPClient} = await import('/front-ivi/online/sdk/WebPortal_IDP_Client.js');
    window.webportal = new IDPClient();

    // Mandatory: Inform the infotainment
    // system that your App is ready
    webportal.set("app.loadingStatus", "running");
    
    // Example of vehicle API:
    // display vehicle VIN
    webportal.get("configuration.VIN")
      .then((message) => {
        let myAppElement = document.getElementById("my-app");
        myAppElement.textContent = "Vehicle VIN: " + message.data;
      })
  })()
  
});

Vehicle APIs #

As seen in JavaScript you can interact with the vehicle using WebPortal SDK.

First of all, we should initialize the SDK:

1
2
3
4
  (async () => {
    const {default : IDPClient} = await import('/front-ivi/online/sdk/WebPortal_IDP_Client.js');
    window.webportal = new IDPClient();
  })()

Then vehicle API are available using the name given to the SDK. They are 3 different type of vehicle APIs:

1
2
3
4
5
6
7
8
/* GET: request data*/
webportal.get()

/* SET: intercat with the vehicle  */
webportal.set()

/* SUBSCRIBE: subscribe to events */
webportal.subscribe()

Method Get

GET method allows requesting single time data.

This method executes the callback function only once, when the request has been resolved successfully or is in error. The callback message includes outputted data or info about the error.

Example: the following API will return the vehicle total mileage.

1
2
3
4
5
6
7
8
9
10
11
12
13
webportal.get(
  /* api name */
  "configuration.VIN",
  /* parameters */
    null
)
/* callback for `REPLY` messages */
.then((message) => {
  if (message.status === 200) {
    /* handle `REPLY` success */
  }
  else { /* handle `REPLY` error */ }
})

false

1
2
3
4
5
6
7
8
9
10
// --- format of the `REPLY` message ---

{
  "id": "7372c16f-5f2f-42c9-84a7-5490e35f1be0",
  "type": "REPLY",
  "api": "configuration.VIN",
  "status": 200,
  "statusText": "OK", 
  "data": "VF7NCRHE8AY539138"
}

false

Check out message callback message format.

Method Set

SET method allows changing a value or set up an action.

This method executes the callback function only once, when the request has been resolved successfully or is in error. The callback message might be empty or includes outputted data or info about the error.

Example: the following API will modify the consumption unit in the vehicle HMI.

1
2
3
4
5
6
7
8
9
10
11
12
13
webportal.set(
  /* api name */
  "configuration.consumptionUnit",
  /* parameters */
  0
)
/* callback for `REPLY` messages */
.then((message) => {
  if (message.status === 200) {
    /* handle `REPLY` success */
  }
  else { /* handle `REPLY` error */ }
})

false

1
2
3
4
5
6
7
8
9
// --- format of the `REPLY` message ---

{
  "id": "7372c16f-5f2f-42c9-84a7-5490e35f1be0",
  "type": "REPLY",
  "api": "configuration.consumptionUnit",
  "status": 200,
  "statusText": "OK"
}

false

Check out message callback message format.

Methode Subscribe

SUBSCRIBE allows to react every time an vehicle event is triggered.

This method executes the callback function once when the request has been taken into account or is in error. Then, the callback function is executed each time the event is triggered. When the subscription fails, only one callback is executed.

The acknowledgment callback message (β€˜type’: β€˜RESULT’) does not include outputted data, but can include info in case of error. All event message callback (β€˜type’: β€˜NOTIFY’) include outputted data or info about error.

Example: the following API will execute a callback function every time the visibility of the App change.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
webportal.subscribe(
  /* api name */
  "app.visibilityState",
  /* parameters */
    null,
  /* callback for `NOTIFY` messages */
  async (notifyMessage) => {
    if (notifyMessage.status === 200) {
      /* handle `NOTIFY` response */
    }
    else { /*  handle `NOTIFY` error  */ }
  }
)
/* callback for `REPLY` messages */
.then((message) => {
  if (message.status === 200) {
    /* handle `REPLY` success */
  }
  else { /* handle `REPLY` error */ }
})

false

1
2
3
4
5
6
7
8
9
10
// --- format of the `REPLY` message ---
// type: acknowledge the status of the subscription
// reception: only once after the subscription request
// contains outputed data: no
{
  "id": "7372c16f-5f2f-42c9-84a7-5490e35f1be0",
  "type": "REPLY",
  "status": 200,
  "statusText": "OK"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// --- format of the `NOTIFY` message ---
// type: event message about the subscription
// reception: each time the conditions of the subscription are met
// contains outputed data: yes
{
  "id": "7372c16f-5f2f-42c9-84a7-5490e35f1be0",
  "type": "NOTIFY",
  "api": "app.visibilityState",
  "status": 200,
  "statusText": "OK", 
  "data": {
    "<appId>": {
      "visibilityState": "hidden"
    }
  } 
}

Check out message callback message format.

Callback Message Format

Name Value Type Value Description
id string n/a This is the unique Id of the request.
type Enum of strings GET, SET or SUBSCRIBE Type of response message.
api string n/a API Name, here is a list of all vehicle APIs.
status int 200 or other code This code inform you if the status of your request is OK (200) or not (other codes).
statusText string n/a This is a message coming with status code.
data string or object n/a This field is where you will receive the data of your request.