This specification defines a WebSocket based API for a Vehicle Information Service (VIS) to enable client applications to 'Get', 'Set', 'Subscribe' and 'Unsubscribe' to vehicle signals and data attributes.

The purpose of the specification is to promote a Server API that enables application development in a consistent manner across participating automotive manufacturers.

Since the previous Working Draft of this specification, the following changes have been brought to the document:

Introduction

This specification describes how a vehicle can expose vehicle signals and static data via a WebSocket. This enables a client to GET or SET vehicle signals and data; to SUBSCRIBE to receive notifications and to UNSUBSCRIBE from receiving notifications.

The API that is used to manage access to vehicle signals and static data is defined in this Vehicle Information Service Specification (VISS). The VISS also describes a discovery mechanism that defines the set of signals and data that a client can access at a particular point in time.

The term 'signal' is used here to represent an item of data that can vary over time, for example vehicle speed, whilst the expressions 'static data' or simply 'data' are used to denote a temporally invariant property like vehicle length.

A vehicle-based software module may implement the interface and behaviours defined in this specification by creating a WebSocket server instance that listens for an inbound connection request from a client in order to enable secure access to vehicle signals. In this specification, this module is referred to as the VIS Server or just 'the server'.

This specification defines a number of methods for accessing vehicle data which are strictly agnostic to the data model. Any data model where data and signals can be specified using a string could potentially be supported. However, this version of the specification specifies that the data model is the GENIVI Vehicle Signal Specification (VSS). VSS supports both extensibility and the ability to define private branches and will be used for each of the examples within this specification.

The version of [[VSS]] is made clear within VSS itself as of version 2.0. In the absence of VersionVSS.Major, VersionVSS.Minor, VersionVSS.Patch and VersionVSS.Label it should be assumed VSS is version 1.0.

In addition, the 'tree' of signals that is accessible at any point in time may also vary depending on standard access control principles. That is, it can vary based on the identity of the user (person or organisation) requesting the data and/or the application or device (e.g. vehicle) where the request originates.

To support this, the VIS Specification describes an extensible token-based security mechanism that can optionally be used to pass tokens to the VIS Server, for example to represent the user of an application and/or the device that the client application is running on.

A future revision of this specification may consider additionally exposing vehicle signals via a RESTful web service but this is out of scope for this version of the specification.

The following example is for illustrative purposes only, it does not show error handling and is not intended to be commercial code.

// Open the WebSocket
var vehicle  = new WebSocket("wss://wwwivi", "wvss1.0");

// Establish authorization
vehicle.onopen = function () {
  vehicle.send('{"action": "authorize", "tokens": {"authorization": "user_token_value"}, "requestId": "103"}');
};

// Message response handler for all possible actions
vehicle.onmessage = function(event){
  var msg = JSON.parse(event.data);
  switch(msg.action){
    case "authorize":
      authHandler(msg);
      break;
    case "getMetadata":
      getMetadataHandler(msg);
      break;
    case "get":
      getReqHandler(msg);
      break;
    case "set":
      setReqHandler(msg);
      break;
    case "subscribe":
      subscriptionSetupHandler(msg);
      break;
    case "subscription":
      subscriptionHandler(msg);
      break;
    case "unsubscribe":
      unsubscribeHandler(msg, false);
      break;
    case "unsubscribeAll":
      unsubscribeHandler(msg, true);
      break;
  }
};

// Auth request handler
function authHandler(msg) {
  if(msg.hasOwnProperty("TTL")){
    console.log("authorization successful");
    requestMetadata();
  } else {
    console.log("authorization unsuccessful");
  }
}

// Close the WebSocket to end the WebSocket session
function closeSocket(){
  vehicle.close();
}
  

Once the WebSocket connection has been established, the client is able to retrieve the metadata from the server and request signals for the client's current level of authentication.

// Request the entire data model, in this example the data model is defined using GENIVI's VSS
function requestMetadata(){
  vehicle.send('{"action": "getMetadata", "requestId": "104"}');
}

// Request the entire data model and if successful, request a signal
function getMetadataHandler(msg){
  if(msg.hasOwnProperty('metadata')){
    console.log("Metadata Received");
    console.log("Metadata: " + JSON.stringify(msg.metadata));
    getSignal(msg.metadata, "Signal.Drivetrain.Transmission.Speed");
  } else {
    console.log("getMetadata Error");
  }
}

// Request a signal
function getSignal(metadata, path){
  // A check could be made here to ensure the signal is available within the metadata
  vehicle.send('{"action": "get", "path": ' + path + ', "requestId": "105"}');
}
  

The client is also able to subscribe to signals if the client's authentication permits access.

// Set a subscription, assuming the same authorization and set up from Example 1
var rpmSubscriptionId, rpmRequestId = "106";

// Set up the subscription
function subscribeToRPMNotifications(){
  vehicle.send('{"action": "subscribe", "path": "Signal.Drivetrain.InternalCombustionEngine.RPM",  "requestId": ' + rpmRequestId + '}');
}

// Handle the subscription response
function subscriptionSetupHandler(msg){
    if(msg.hasOwnProperty("requestId") && msg.requestId == rpmRequestId){
      console.log("Subscription set with a subscription ID of " + msg.subscriptionId);
      rpmSubscriptionId = msg.subscriptionId;
    }
}

// Handle the subscription notification
function subscriptionHandler(msg){
    if(msg.hasOwnProperty("subscriptionId") && msg.subscriptionId == rpmSubscriptionId){
      console.log("The current engine rpm is " + msg.value);
    }
}

The target platform supported by the specification is exclusively passenger vehicles. Use of this specification for non-passenger applications (for example heavy machinery, marine and airline infotainment) is not prohibited, but is not covered in the design or content of the API.

An example use case could be the implementation of a 'Home Mechanic' application that provides warnings if any the following need attention: tire pressure, engine oil level, washer fluid level and battery charge level.

Exposing vehicle signals in a standardized manner facilitates the integration of vehicles within the Web of Things (WoT) and supports future use case innovations in a variety of fields including transportation, safety, navigation, energy management, smart transportation and consumer infotainment.

The data format used for client/server communication within this specification is JSON encoded strings, however other data formats may be supported in future versions of the specification.

This specification defines conformance criteria that apply to a single product: specifically the 'in-vehicle' WebSocket Vehicle Information Service that implements the interfaces, semantics and behaviour defined in this document.

Terminology

The acronym 'VISS' is used to refer to this document, the 'Vehicle Information Service Specification'.

The acronym '[[VSS]]' is used to refer to the 'Vehicle Signal Specification' which is defined by the GENIVI Alliance.

The term 'WebSocket' when used in this document, is as defined in the {{WebSocket}} and the [[RFC6455]].

Architecture

Overview

In a typical vehicle design, signals and data are transmitted between Electronic Control Units (ECUs) connected via internal vehicle networks. These include Controller Area Networks (CAN), Media Oriented Systems Transport (MOST) and Local Interconnect Networks (LIN). ECUs on these networks broadcast messages on network buses, and other ECUs on the bus respond to the messages.

In the component diagram included in Figure 2, the internal vehicle CAN, MOST and LIN networks and the ECUs that communicate via these networks are abstracted and for simplicity, represented by the System component.

For safety, security and commercial reasons not all clients should be able to GET, SET or SUBSCRIBE to particular vehicle signals and data attributes. As a consequence, access-control must be managed so clients cannot simply connect directly to ECUs or to CAN, MOST or LIN network buses.

The vehicle signals and static data that are available within the vehicle system are exposed in a controlled manner to the VIS Server. The interface and communication mechanisms that are used between the vehicle system and the VIS Server are outside of the scope of this specification.

Vehicle Information Service (VIS) Server

As stated above, the VIS Server is the 'in-vehicle' system that is responsible for exposing vehicle signals and data to on-board clients by implementing the interface and behaviours defined in this Vehicle Information Service Specification (VISS).

The VIS Server exposes signals and data in a manner that is consistent with the accompanying data model. The data model is not mandated by this specification, however the recommended data model used within this specification is the Vehicle Service Specification (VSS). This defines a 'tree-like' logical taxonomy of the vehicle, (formally a Directed Acyclic Graph), where major vehicle structures (e.g. body, engine) are near the top of the tree and the logical assemblies and components that comprise them, are defined as their child nodes. Each of the child nodes in the tree is further decomposed into its logical constituents, and the process is repeated until leaf nodes are reached. A leaf node is a node at the end of a branch that cannot be decomposed because it represents a single signal or data attribute value. An example VSS tree is shown below, its contents are illustrative:

Diagram showing an example Vehicle Signal tree
Diagram showing an example Vehicle Signal Tree

Within GENIVI's VSS data model, signals are named according to their path using the dot notation e.g. engine.rpm. The methods defined within this document support any path notation which can be represented as a string.

The client may invoke the VIS Server's getMetadata method to request that the server returns metadata that describes which signals and data attributes could potentially be accessed provided that the user and/or device is suitably authorized. This and other valid VIS Server actions are defined in more detail here.

In-Vehicle Clients

In-vehicle clients include both those clients that are running on an ECU in the vehicle itself, e.g. an ECU that implements an In-Vehicle Infotainment (IVI) system, but also includes clients running on a user's device, e.g. a laptop, phone or tablet, that is connected via the vehicle's WiFi client (if one exists).

Component diagram showing VIS Server relationships with vehicle system and clients
Diagram showing relationships between the vehicle system, VIS Server and its clients.

The methods defined on the VIS Server interface may be invoked by different types of on-board and off-board clients. On-board clients, running on the vehicle fall into one of two major categories:

Applications running in the vehicle that can access vehicle signals and data and have a user interface that the driver or passengers in the vehicle can interact with.

Agents typically have no user interface. They can also invoke methods exposed on the VIS Server but their purpose is to connect to one or more off-board e.g. Vehicle to Everything (V2X) internet Servers in order to send data off-board.

Both applications and agents can be subdivided into those that are web-based and which run in a 'Web Runtime' and are implemented using Web Standards like HTML, CSS and JavaScript; those that run in their own native process written using e.g. C, C++ or Objective-C; and those that run in a Managed Runtime process written using languages like C# or Java.

Web applications and web agents may directly invoke the Vehicle Information Service interface exposed by the VIS Server or implement a JavaScript 'wrapper' library to simplify accessing vehicle signals and data in a controlled way.

Sending Signals and Data off-board

In addition to local, in-vehicle applications and agents, a variety of internet-based clients and servers may be interested in accessing vehicle signals and data. However, when a vehicle is not being used, most electrical systems are shut down in order to maximize battery life. This may include the systems that enable Wireless or Mobile connectivity. When the vehicle is powered up, various systems start up, including potentially those that provide off-board connectivity and the vehicle typically connects either to a local WiFi network or to a Mobile Network Operator (MNO) and is dynamically assigned an IP address.

At this point, internet-based clients and servers do not know the dynamic IP address that was assigned to a specific vehicle. So normally, a vehicle has to connect to a well-known endpoint, generally using a URL to connect to a V2X Server. The vehicle and the internet server typically mutually authenticate and the vehicle 'registers' with the V2X server over an encrypted channel passing it a unique identifier e.g. its Vehicle Identification Number (VIN). From that point on, the V2X server has the IP address that is currently assigned to a vehicle with a particular VIN, and can share this information with other internet-based clients and servers, which are then able to send messages to the vehicle.

However, if the vehicle loses connectivity and is dynamically assigned a new IP address, it needs to re-register its new IP address and the new address needs to be communicated to any interested parties. For simplicity, this is not shown in the component diagram, primarily because this connectivity only becomes possible after the vehicle has registered with at least one off-board server (which is shown) but also because these scenarios do not affect the interface and behaviours defined in this Vehicle Information Service Specification.

The component diagram shows four different types of internet-based clients. A user may use a Web Page or a Web Application running on a phone or tablet connected to the vehicle's ethernet network to request access to vehicle signals on a particular vehicle. Alternatively, these signals could be accessed using a Native or Managed Runtime Application or via another automated Client System or service, for example an onboard agent communicating with a Traffic Management System in a 'Smart City'.

VIS Server State Diagram

The following diagram shows a number of possible states that the VIS Server could occupy:

State Diagram for VIS Server
State diagram for the VIS Server

The state diagram is for illustrative purposes only and does not assume that the VIS Server is either single or multi-threaded. The implementer of the VIS Server is free to determine its internal design and implementation, however in normal operation, it is assumed that the server will be able to accept multiple requests in quick succession and that the VIS Server will attempt to process and respond to each request in accordance with the specified API.

Security and Privacy Considerations

Introduction

The VIS Server implementation MAY optionally restrict access to one or more vehicle signals so that they can only be accessed in response to a request from an authorized user and/or device. This could be for a variety of reasons, including safety, privacy or commercial considerations.

Hence, a request to GET, SET, SUBSCRIBE or UNSUBSCRIBE to data may require the client to demonstrate to the server that the request is from one or more suitably authorized Security Principals.

The different types of Security Principal; the approach taken to control access to signals and the importance of data privacy are described in the sections that follow.

Security Principals

The types of security principal SHALL include:

Type Description
User A person, system or organisation responsible for making the request e.g. driver, Emergency Services, Smart City Traffic Management System.
Device Vehicle or device where the request originates. Could for example be a user's Consumer Electronics (CE) device connected to the vehicle's WiFi hotspot or another vehicle in a convoy; an Electronic Control Unit (ECU) on the same vehicle or any internet connected system e.g. a Web of Things (WoT) device.

Access Control and Authorization

When a client makes a request to access signal data it is performing the request on behalf of one or more Security Principals, for example for a particular user and/or vehicle/device.

Access to signals is managed and controlled by the server. The server MAY elect not to enforce access controls on a particular signal or set of signals and to enforce different access controls on other signals.

For each security principal that must be authorized by the server, the client SHALL obtain and pass a security token e.g. an OAuth 2.0 token (see RFC6749) to the server using a message containing an authorize action.

A server implementation MAY require that a request for a particular signal includes a security token for both the user (e.g. driver of the vehicle) and for the device (e.g. the vehicle) that is hosting the client. A request for a different set of vehicle signals MAY require that only the user is authorized or that only the device is authorized to access particular signals.

The client and the server are each individually responsible for implementing security best practices. This includes but is not limited to securely obtaining and verifying tokens (as applicable) and the prevention of replays or spoofing of tokens by malicious actors or agents.

WebSocket Channel Authorization

The client MAY send a message with an authorize action to modify the access-control state of the WebSocket channel. The message structure is defined in detail here.

The following diagram illustrates a scenario where the client requests vehicle speed. In this example scenario, this signal is not under access control and so the server returns a message containing the requested data. The client then requests that the server sets the vehicle trunk status to open and the server demands that the client passes access-control credentials before satisfying the request. The sequence of steps is illustrated in the diagram below:

WebSocket Security token flow
Diagram showing conceptual WebSocket Security Token flow

After receiving a message with an authorize action the server attempts to verify the tokens e.g. by checking with the issuing Security Authority. If all of the tokens that are passed to the server are valid, it returns a success response and all subsequent requests received by the WebSocket instance have elevated access control privileges. Specifically, each of the GET, SET, SUBSCRIBE and UNSUBSCRIBE actions have the access control rights that the server deems to be appropriate for the security principals represented by the token(s).

If the client sends a subsequent authorize message to the server with different token value(s), if one or more of these are invalid, then the server SHALL return an error response and the WebSocket access control status remains unchanged. However, if the new token values are valid, then the server returns a success response and the access control privileges associated with the new token(s) only, applies for all requests from that point.

If the client or the server close the WebSocket connection and a new WebSocket instance is opened, then it is opened without elevated access control privileges.

This service specification defines a standardized, token-based approach for access control that includes specific error responses for common user and device access control scenarios. It is important however, that the security model is extensible. Hence, the server MAY implement any type of token that is consistent with this standardized approach and MAY optionally define additional token(s) for other Security Principal type(s). If this is the case, it is expected that the precise type of security tokens that are supported by a particular server implementation, the format of those tokens and all additional error codes and reasons (e.g. to indicate that an additional token type has expired and needs to be renewed) SHALL be defined in the Server's documentation.

Use of Encryption

To support a layered security model and to help establish a 'defense in depth', all vehicle signal and data communications between the client and server MUST be strongly encrypted. This is to make it more difficult for an attacker to eavesdrop or tamper with the security tokens; the request data or the response payload.

One way in which this may be implemented is for the client and the server to use a Public Key Infrastructure (PKI) approach, where the client verifies the server's identity by checking the server's X.509 certificate and the client and the server negotiate to establish a secure Transport Layer Security (TLS) 'tunnel'.

The WebSocket protocol mandates that if a client requests that the server opens a WebSocket connection and the request is received over HTTPS, then the WebSocket is established over TLS, that is, a secure 'wss' connection is created.

Token Renewal

Each security token SHALL have a specified lifetime during which it is valid. If on receiving a request for signals that are subject to access-control, the server determines that the request is unauthorized because the token has expired, the server SHALL return an error response indicating the fact and the client MAY request a new token from the Security Authority and repeat the request.

If the server returns an error response indicating that the request is forbidden, renewing the security token would not make the request valid. In this case, the client should not repeat the request unless some other change has been made that may mean the request is now valid.

Initialisation of the WebSocket

If the client application is an HTML Application running in a web runtime or is a web page running in a browser, the WebSocket instance may either be instantiated natively or be created using a 'standards-compliant' WebSocket JavaScript library.

A WebSocket can also be initiated from a native (e.g. C++) Application or from an Application written using a 'Managed Runtime' language like Java or C#. It is assumed that native and managed clients use a suitable standards-compliant WebSocket library to request that a WebSocket connection is opened on the server.

Implementations that support additional devices or multiple VIS services should provide discovery. Alternatively, the location of a particular VIS Server instance on the local vehicle network may be handled by configuration, either as part of a package manifest or by consulting a registry on application install. The 'wwwivi' hostname in this specification is used an example.

A client running on the vehicle is able to connect to the VIS Server instance using the hostname e.g. 'wwwivi' and uses the default port 443. The hostname 'wwwivi' may locally be mapped to the localhost IP address 127.0.0.1 e.g. by adding an entry to the /etc/hosts file.

The sub-protocol name SHALL be 'wvss' with a version number suffix, e.g. wvss1.0. The sub-protocol version will be associated with exactly one Vehicle Server Specification (VSS) version so that the client and server can correctly validate and parse request and response message packets.

	  var vehicle  = new WebSocket("wss://wwwivi", "wvss1.0");
	

The client SHALL connect to the server over HTTPS and request that the server opens a WebSocket. All WebSocket communications between the client and server MUST be over ​Secure WebSocket connection with URI-schema ​‘wss’. Non-encrypted communication is not supported, hence the server MUST refuse Insecure WebSocket connections with URI-schema ‘ws’ connection requests.

This specification assumes that a single WebSocket is used to enable communication between a client application and the server. It is not explicitly prohibited for the client to request that the server opens more than one WebSocket, however, the server MAY refuse to open a subsequent WebSocket connection and the client is responsible for handling this gracefully.

If more than one WebSocket connection is established between a client application and the server then each connection MUST be managed independently. For example, subscriptions created using a particular WebSocket connection shall only trigger notifications via that connection and the client MUST use that WebSocket connection to unsubscribe.

If more than one WebSocket connection has been established between one or more clients and a particular server instance, there is a risk that race conditions and concurrency issues could occur. An example of this would be where two or more WebSocket connections are used to update a particular setting at the same time.

Unless explicitly stated otherwise, the client MAY only assume that the server implements a simple concurrency model where lost updates and dirty reads could potentially occur if the server has more than one WebSocket connection open.

Message Structure

The client MUST use the {{WebSocket}} send method, to pass request messages to the server. The message signature SHALL be:

		WebSocket.send(request)
	

The request message MUST be comprised of one of the request objects defined in this section. The client SHAsLL receive responses from the server using the WebSocket onmessage method, as follows:

		WebSocket.onmessage = function(obj){
			// process data
		}
	

The message returned by the server MUST be one of the response objects defined in the table below.

Request Objects Response Objects
authorizeRequest authorizeSuccessResponse
authorizeErrorResponse
metadataRequest metadataSuccessResponse
metadataErrorResponse
getRequest getSuccessResponse
getErrorResponse
setRequest setSuccessResponse
setErrorResponse
subscribeRequest subscribeSuccessResponse
subscribeErrorResponse
subscriptionNotification
subscriptionNotificationError
unsubscribeRequest unsubscribeSuccessResponse
unsubscribeErrorResponse
unsubscribeAllRequest unsubscribeAllSuccessResponse
unsubscribeAllErrorResponse

The request and response parameters contain a limited number of attributes, defined in the table below.

Term Definitions

Attribute Type Description
action Action The type of action requested by the client or delivered by the server.
path String The path to the desired vehicle signal(s), as defined by the Vehicle Signal Specification (VSS).
requestId String Unique id value specified by the client. Returned by the server in the response and used by the client to link the request and response messages. The value MAY be an integer or a Universally Unique Identifier (UUID).
subscriptionId String Value returned by the server to uniquely identify each subscription. The value MAY be an integer or a Universally Unique Identifier (UUID).
tokens object Structure containing one or more security token (e.g OAuth2) name/value pairs.
timestamp integer The Coordinated Universal Time (UTC) time that the server returned the response (expressed as the number of milliseconds).
value any The data value returned by the server. This could either be a basic type, or a complex type comprised of nested name/value pairs in JSON format.
TTL integer Returns the time to live of the authorization token in seconds.
filters object Provides a filtering mechanism to reduce the demands of a subscription on the server.
metadata object Metadata describing the potentially available signal tree.
error Error Returns an error code, reason and message.

JSON Schema Definitions

The definitions within this section describe the datatypes referenced within the JSON Schema VISS interfaces.

{
    "definitions": {
        "action": {
            "enum": [ "authorize", "getMetadata", "get", "set", "subscribe", "subscription", "unsubscribe", "unsubscribeAll"],
            "description": "The type of action requested by the client and/or delivered by the server"
        },
        "requestId": {
            "description": "Returned by the server in the response and used by the client to link the request and response messages.",
            "type": "string"
        },
        "path": {
            "description": "The path to the desired vehicle signal(s), as defined by the metadata schema.",
            "type": "string"
        },
        "value": {
            "description": "The data value returned by the server. This could either be a basic type, or a complex type comprised of nested name/value pairs in JSON format."
        },
        "timestamp": {
            "description": "The Coordinated Universal Time (UTC) time that the server returned the response (expressed as number of milliseconds).",
            "type": "integer"
        },
        "filters": {
            "description": "May be specified in order to throttle the demands of subscriptions on the server.",
            "type": ["object", "null"],
            "properties": {
                "interval": {
                    "description": "The server is requested to provide notifications with a period equal to this field's value.",
                    "type": "integer"                   
                },
                "range": {
                    "description": "The server is requested to provide notifications only whilst a value is within a given range.",
                    "type": "object",
                    "properties":{
                      "below": {
                        "description": "The server is requested to provide notifications when the value is less than or equal to this field's value.",
                        "type": "integer"  
                      },
                      "above": {
                        "description": "The server is requested to provide notifications when the value is greater than or equal to this field's value.",
                        "type": "integer"  
                      }
                    }                   
                },
                "minChange": {
                    "description": "The subscription will provide notifications when a value has changed by the amount specified in this field.",
                    "type": "integer"                   
                }
            }
        },
        "subscriptionId":{
            "description": "Integer handle value which is used to uniquely identify the subscription.",
            "type": "string"
        },
        "metadata":{
            "description": "Metadata describing the potentially available signal tree.",
            "type": "object"
        },
        "error": {
            "description": "Server response for error cases",
            "type": "object",
            "properties": {
                "number": {
                    "description": "HTTP Status Code Number",
                    "type": "integer"                   
                },
                "reason": {
                    "description": "Pre-defined string value that can be used to distinguish between errors that have the same code",
                    "type": "string"                    
                },
                "message": {
                    "description": "Message text describing the cause in more detail",
                    "type": "string"                    
                }
            }   
        }
    }
}

Action

The Action enumeration is used to define the type of action requested by the client. All client messages MUST contain a JSON structure that has an action name/value pair and the value of the action property MUST be one of the values specified in the enumeration:

authorize
Enables client to pass security tokens for Security Principals to the server to support access-control.
getMetadata
Allows the client to request metadata describing signals and data attributes that are potentially accessible.
get
Enables the client to get one or more values once.
set
Enables the client to set one or more values once.
subscribe
Enables the client to request notifications containing a JSON data structure with values for one or more vehicle signals and/or data attributes. The client requests that it is notified when the signal changes on the server.
subscription
Enables the server to send notifications to the client containing a JSON data structure with values for one or more vehicle signals and/or data attributes.
unsubscribe
Allows the client to notify the server that it should no longer receive notifications based on that subscription.
unsubscribeAll
Allows the client to notify the server that it should no longer receive notifications for any active subscription.

Authorize

To enable access to signals and data attributes that are under access control, the client MAY optionally pass a message with an authorize action to the server. The structure of the message and the associated success and error responses are defined below.

Authorize Request

The authorizeRequest has the following properties and schema:

Object NameAttributeTypeRequired
authorizeRequest
actionActionYes
tokensobjectYes
requestIdstringYes
{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Authorize Request",
    "description": "Enables the client to pass security token(s) to the server that can be used to authorize access to signals and data",
    "type": "object",
    "required": ["action", "tokens", "requestId"],
    "properties": {
        "action": {
            "enum": [ "authorize" ],
            "description": "The identifier for the authorize request"
        },
        "tokens": {
            "description": "Extensible key-value pair token mechanism used for access control",
            "type": "object",
            "properties": {
                "authorization": {
                    "description": "The user token, for the user that the client is making requests on behalf of",
                    "type": "string"
                },
                "www-vehicle-device": {
                    "description": "The device token for the originating device that is making the request to the server",
                    "type": "string"
                }
            }
        },
        "requestId": {
            "$ref": "#/definitions/requestId"
        }
    }
}

The authorizeSuccessResponse has the following properties and schema:

Object NameAttributeTypeRequired
authorizeSuccessResponse
actionActionYes
TTLintegerYes
requestIdstringYes
    
{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Authorize Success Response",
    "description": "The response sent from the server upon a successful authorization request",
    "type": "object",
    "required": ["action", "TTL", "requestId"],
    "properties": {
        "action": {
            "enum": [ "authorize" ],
            "description": "The identifier for the authorize request"
        },
        "TTL": {
            "description": "The time to live of the authorization token",
            "type": "integer"
        },
        "requestId": {
            "$ref": "#/definitions/requestId"
        }
    }
}

The authorizeErrorResponse has the following properties and schema:

Object NameAttributeTypeRequired
authorizeErrorResponse
actionActionYes
errorErrorYes
requestIdstringYes
  
{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Authorize Error Response",
    "description": "The response sent from the server upon an unsuccessful authorization request",
    "type": "object",
    "required": ["action", "error", "requestId"],
    "properties": {
        "action": {
            "enum": [ "authorize" ],
            "description": "The identifier for the authorize request"
        },
        "error": {
            "$ref": "#/definitions/error"
        },
        "requestId": {
            "$ref": "#/definitions/requestId"
        }
    }
}

The server SHALL provide support the following security token types and names:

Security Principal Token Name Description
User authorization The user that the client is making requests on behalf of. This MAY be a person e.g. driver or passenger, it MAY be an organisation e.g. Emergency Services or MAY be any other legal entity.
Device www-vehicle-device The originating device that is making the request to the server. This MAY be an ECU in the vehicle that is hosting the VIS Server or MAY be a device that is connected to the vehicle via a WiFi hotspot or MAY be any other device.

The 'tokens' JSON fragment MAY contain an 'authorization' structure that contains just a single name/value pair, for example, to pass only the user token. Or it may contain only the 'www-vehicle-device' name/value pair to pass the just the vehicle token; alternatively it MAY include name/value pairs for both the 'authorization' and 'www-vehicle-device' tokens. These alternatives are illustrated in the following example:

		if(userTokenOnly){
			// Pass user token only
			vehicle.send('{ "action": "authorize",
				"tokens": { "authorization": "<user_token_value>" },
				"requestId": "<some_unique_value>" }');

		} else if (deviceTokenOnly) {
			// Pass vehicle/device token only
			vehicle.send('{ "action": "authorize",
				"tokens": { "www-vehicle-device": "<device_token_value>" },
				"requestId": "<some_unique_value>" }');

		} else if (userAndDeviceToken) {
			// Pass tokens for user and device
			vehicle.send('{ "action": "authorize",
				"tokens": { "authorization": "<user_token_value>",
					"www-vehicle-device": "<device_token_value>" },
				"requestId": "<some_unique_value>" }');
		}
	

This specification purposely does not define the token structure and the methods to obtain token(s). Providers of the server implementation may select their own preferred token format and method for verifying the authenticity of token(s) passed to the server. The server may also treat tokens as opaque structures and pass them on to underlying software layers for evaluation.

It is expected that client and server use the same token format. If a client presents a token using a format that is not understood by the server, the server rejects the token.

In order to make dictionary attacks more difficult, authentication will be denied after a number of failed authentication requests. A 401 (Unauthorized) too_many_requests error will be sent by the server in response to any subsequent authentication requests. The number of failed requests and the period of time during which attempts will be denied is to be determined by the implementation.

Metadata

The client MAY use the getMetadata action to request metadata describing the potentially available signal schema, for example the VSS tree. It does this by sending a metadataRequest message to the server. If the server is able to return the metadata, then this is returned using a metadataSuccessResponse message. If an error occurs, the server returns a metadataErrorResponse message to the client.

The client is able to request metadata from any point in the signal tree, such that only the metadata for the signals within the given branch of the schema's hierarchy is returned. For example, only metadata for the chassis branch of the VSS tree is returned when the chassis path is specified. If the path is not set, the response contains the metadata for the entire signal tree.

If more than one getMetadata call is made with the same metadataRequest message content but at different times, the metadata in the metadataSuccessResponse SHALL be the same provided the access-control state of the WebSocket channel has not changed.

The Vehicle Signal Server (VSS) specification allows branches to be defined as either public or private. The VIS server will satisfy a request for metadata in a public branch regardless of the access control permissions associated with the client making the request. This means that a user could see that a particular signal in a public branch is available but they may not be currently authorized to 'Get', 'Set', or 'Subscribe' to that signal. If a branch is defined to be 'private', only suitably authorized clients will be able to retrieve metadata for that branch. This is to enable vehicle manufacturers to apply access controls to metadata for commercially sensitive signals and data.

If a signal is requested to which the client has no access then the server will reject the request as detailed by the 'Get', 'Set', and 'Subscribe' sections of this specification. The server shall only return metadata for branches and nodes which are classified as private if the client has appropriate access permissions.

Metadata Request

The metadataRequest has the following properties and schema:

Object NameAttributeTypeRequired
metadataRequest
actionActionYes
pathstringYes
requestIdstringYes
{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Metadata Request",
    "description": "Request metadata describing the potentially available signals",
    "type": "object",
    "required": ["action", "path", "requestId"],
    "properties": {
        "action": {
            "enum": [ "getMetadata" ],
            "description": "The identifier for the getMetadata request"
        },
        "path": {
            "$ref": "#/definitions/path"
        },
        "requestId": {
            "$ref": "#/definitions/requestId"
        }
    }
}

The metadataSuccessResponse has the following properties and schema:

Object NameAttributeTypeRequired
metadataSuccessResponse
actionActionYes
requestIdstringYes
metadataobjectYes
timestampintegerYes
{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Metadata Success Response",
    "description": "The response sent from the server upon a successful getMetadata request",
    "type": "object",
    "required": ["action", "requestId", "metadata", "timestamp"],
    "properties": {
        "action": {
            "enum": [ "getMetadata" ],
            "description": "The identifier for the getMetadata request"
        },
        "requestId": {
            "$ref": "#/definitions/requestId"
        },
        "metadata": {
            "$ref": "#/definitions/metadata"
        },
	"timestamp": {
	    "$ref": "#/definitions/timestamp"
    	}
    }
}

The metadataErrorResponse has the following properties and schema:

Object NameAttributeTypeRequired
metadataErrorResponse
actionActionYes
requestIdstringYes
errorErrorYes
timestampintegerYes
{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Metadata Error Response",
    "description": "The response sent from the server upon an unsuccessful getMetadata request",
    "type": "object",
    "required": ["action", "requestId", "error", "timestamp"],
    "properties": {
        "action": {
            "enum": [ "getMetadata" ],
            "description": "The identifier for the getMetadata request"
        },
        "requestId": {
            "$ref": "#/definitions/requestId"
        },
        "error": {
            "$ref": "#/definitions/error"
        },
	"timestamp": {
	    "$ref": "#/definitions/timestamp"
    	}
    }
}

Examples

The following data flow example shows a request for the signal structure within the Signal.Drivetrain.InternalCombustionEngine.RPM branch containing the vehicle RPM signal, where the metadata is defined using a VSS tree. A leaf node has been chosen in this example for brevity, however entire VSS branches and other schemas can also be requested using the getMetadata interface.

  client -> {
    "action": "getMetadata",
    "path": "Signal.Drivetrain.InternalCombustionEngine.RPM",
    "requestId": "3874"
  }
  receive <- {
    "action": "getMetadata",
    "requestId": "3874",
    "metadata": { "Signal": {
        "description": "All signals that can dynamically be updated by the vehicle",
        "type": "branch",
        "children": {
          "Drivetrain": {
            "description": "Drivetrain data for internal combustion engines, transmissions, electric motors, etc.",
            "type": "branch",
            "children": {
              "InternalCombustionEngine": {
                "description": "Engine-specific data, stopping at the bell housing.",
                "type": "branch",
                "children": {
                  "RPM": {
                    "description": "Engine speed measured as rotations per minute.",
                    "min": 0,
                    "max": 20000,
                    "type": "UInt16",
                    "id": 54,
                    "unit": "rpm"
                  }
                }
              }
            }
          }
        }
      }
    },
    "timestamp": 1496087968995
  }
  

The following data flow example shows a request for the VSS structure within the Attribute.Body branch of the VSS. The example assumes the VSS contains two leaf nodes within the Attribute.Body branch.

  client -> {
    "action": "getMetadata",
    "path": "Attribute.Body",
    "requestId": "3875"
  }
  receive <- {
    "action": "getMetadata",
    "requestId": "3875",
    "metadata": { "Attribute": {
        "description": "Attribute signals that do not change during the power cycle of a vehicle.",
        "type": "branch",
        "children": {
          "Body": {
            "description": "All body components",
            "type": "branch",
            "children": {
              "BodyType": {
                "description": "Body type code as defined by ISO 3779",
                "type": "string"
              },
              "RefuelPosition": {
                "description": "Location of the fuel cap or charge port",
                "type": "string",
                "enum": ["front_left", "front_right", "middle_left", "middle_right", "rear_left", "rear_right"]
              }
            }
          }
        }
      }
    },
    "timestamp": 1489985044000
  }
  

Get

The client MAY send a getRequest message to the server to get the value of one or more vehicle signals and data attributes. If the server is able to satisfy the request it SHALL return a getSuccessResponse message. If the server is unable to fulfill the request, e.g. because the client is not authorized to retrieve one or more of the signals, then the server SHALL return a getErrorResponse. The structure of these message objects is defined below:

Get Request

The getRequest has the following properties and schema:

Object NameAttributeTypeRequired
getRequest
actionActionYes
pathstringYes
requestIdstringYes
{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Get Request",
    "description": "Get the value of one or more vehicle signals and data attributes",
    "type": "object",
    "required": ["action", "path", "requestId"],
    "properties": {
        "action": {
            "enum": [ "get" ],
            "description": "The identifier for the get request"
        },
        "path": {
            "$ref": "#/definitions/path"
        },
        "requestId": {
            "$ref": "#/definitions/requestId"
        }
    }
}

The getSuccessResponse has the following properties and schema:

Object NameAttributeTypeRequired
getSuccessResponse
actionActionYes
requestIdstringYes
valueobjectYes
timestampintegerYes
{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Get Success Response",
    "description": "The response sent from the server upon a successful get request",
    "type": "object",
    "required": ["action", "requestId", "value", "timestamp"],
    "properties": {
        "action": {
            "enum": [ "get" ],
            "description": "The identifier for the get request"
        },
        "requestId": {
            "$ref": "#/definitions/requestId"
        },
        "value": {
            "$ref": "#/definitions/value"
        },
        "timestamp": {
            "$ref": "#/definitions/timestamp"
        }
    }
}

The getErrorResponse has the following properties and schema:

Object NameAttributeTypeRequired
getErrorResponse
actionActionYes
requestIdstringYes
errorErrorYes
timestampintegerYes
{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Get Error Response",
    "description": "The response sent from the server upon an unsuccessful get request",
    "type": "object",
    "required": ["action", "requestId", "error", "timestamp"],
    "properties": {
        "action": {
            "enum": [ "get" ],
            "description": "The identifier for the get request"
        },
        "requestId": {
            "$ref": "#/definitions/requestId"
        },
        "error": {
            "$ref": "#/definitions/error"
        },
        "timestamp": {
            "$ref": "#/definitions/timestamp"
        }
    }
}

Examples

It is important to note that all examples involving paths are illustrative. Valid path values and the signals and data attributes that correspond to a particular path are defined in the Vehicle Signal Specification.

The example below shows the JSON structure for a getRequest message sent by the client to obtain the engine RPM value and a getSuccessResponse returned by the server.

	client -> {
		"action": "get",
		"path": "Signal.Drivetrain.InternalCombustionEngine.RPM",
		"requestId": "8756"
	}

	receive <- {
		"action": "get",
		"requestId": "8756",
		"value": 2372,
		"timestamp": 1489985044000
	}
	

In the case where the server returns a value that is a complex type, i.e. a value that is not a single basic JavaScript type (e.g. String, Number, Boolean), the value SHALL be returned as a set of name/value pairs in a JSON object structure. The format MUST be as defined by the version of the Vehicle Signal Specification that is associated with the WebSocket sub-protocol value specified when the WebSocket is created.

The following shows an example of a getRequest that results in the server returning a getSuccessResponse with a value that is a complex type:

	client -> {
		"action": "get",
		"path": "Signal.Body.Trunk",
		"requestId": "9078"
	}

	receive <- {
		"action": "get",
		"requestId": "9078",
		"value": { "Signal.Body.Trunk.IsLocked": false,
			"Signal.Body.Trunk.IsOpen": true },
		"timestamp": 1489985044000
	}
	

One or more wildcards (denoted by asterisk '*') MAY be included at any level in the path to specify that all nodes at that level are to be included.

In the example below, the path in the getRequest includes a wildcard at the levels above the leaf (signal) node, in order to request just the 'IsLocked' state for all doors.

	client -> {
		"action": "get",
		"path": "Signal.Cabin.Door.*.IsLocked",
		"requestId": "4523"
	}

	receive <- {
		"action": "get",
		"requestId": "4523",
		"value": [ {"Signal.Cabin.Door.Row1.Right.IsLocked" : true },
		           {"Signal.Cabin.Door.Row1.Left.IsLocked" : true },
			       {"Signal.Cabin.Door.Row2.Right.IsLocked" : false },
			       {"Signal.Cabin.Door.Row2.Left.IsLocked" : true } ],
		"timestamp": 1489985044000
	}
	

In this example, a complex type with a nested array is returned in response to the Path: "Signal.Cabin.Door.*" which denotes: 'Return all signals and data attributes for all doors'. For simplicity, the example assumes that the VSS definition for each door only has two attributes 'IsLocked' and 'Window.Position'.

	client -> {
		"action": "get",
		"path": "Signal.Cabin.Door.*",
		"requestId": "6745"
	}

	receive <- {
		"action": "get",
		"requestId": "6745",
		"value": [ {"Signal.Cabin.Door.Row1.Right.IsLocked" : true, "Signal.Cabin.Door.Row1.Right.Window.Position": 50},
		           {"Signal.Cabin.Door.Row1.Left.IsLocked" : true, "Signal.Cabin.Door.Row1.Left.Window.Position": 23},
		           {"Signal.Cabin.Door.Row2.Right.IsLocked" : false, "Signal.Cabin.Door.Row2.Right.Window.Position": 100 },
		           {"Signal.Cabin.Door.Row2.Left.IsLocked": true, "Signal.Cabin.Door.Row2.Left.Window.Position": 0 } ],
		"timestamp": 1489985044000
	}
	

The following shows a request for non-existent data

	client -> {
		"action": "get",
		"path": "Body.Flux.Capacitor",
		"requestId": "1245"
	}

	receive <- {
		"action": "get",
		"requestId": "1245",
		"error": { "number":404,
			"reason": "invalid_path",
			"message": "The specified data path does not exist." },
		"timestamp": 1489985044000
	}
	

Set

The client may request that the server sets the value of one or more signals e.g. to lock one or more doors or open a window by sending a setRequest message to the server. If the server is able to satisfy the request it SHALL return a setSuccessResponse message. If an error occurs e.g. because the client is not authorized to set the requested value, or the value is read-only, the server SHALL return a setErrorResponse message.

Set Request

The setRequest has the following properties and schema:

Object NameAttributeTypeRequired
setRequest
actionActionYes
pathstringYes
valueanyYes
requestIdstringYes
{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Set Request",
    "description": "Enables the client to set one or more values once.",
    "type": "object",
    "required": ["action", "path", "value", "requestId"],
    "properties": {
        "action": {
            "enum": [ "set" ],
            "description": "The identifier for the set request"
        },
        "path": {
            "$ref": "#/definitions/path"
        },
        "value": {
            "$ref": "#/definitions/value"
        },
        "requestId": {
            "$ref": "#/definitions/requestId"
        }
    }
}

The setSuccessResponse has the following properties and schema:

Object NameAttributeTypeRequired
setSuccessResponse
actionActionYes
requestIdstringYes
timestampintegerYes
{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Set Success Response",
    "description": "The response sent from the server upon a successful set request",
    "type": "object",
    "required": ["action", "requestId", "timestamp"],
    "properties": {
        "action": {
            "enum": [ "set" ],
            "description": "The identifier for the set request"
        },
        "requestId": {
            "$ref": "#/definitions/requestId"
        },
        "timestamp": {
            "$ref": "#/definitions/timestamp"
        }
    }
}

The setErrorResponse has the following properties and schema:

Object NameAttributeTypeRequired
setErrorResponse
actionActionYes
requestIdstringYes
errorErrorYes
timestampintegerYes
{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Set Error Response",
    "description": "The response sent from the server upon an unsuccessful set request",
    "type": "object",
    "required": ["action", "requestId", "error", "timestamp"],
    "properties": {
        "action": {
            "enum": [ "set" ],
            "description": "The identifier for the set request"
        },
        "requestId": {
            "$ref": "#/definitions/requestId"
        },
        "error": {
            "$ref": "#/definitions/error"
        },
        "timestamp": {
            "$ref": "#/definitions/timestamp"
        }
    }
}

Examples

Successfully set a signal.

    client -> {
      "action": "set",
      "path": "Signal.Cabin.Door.*.IsLocked",
      "value": [ {"Row1.Right.IsLocked": true },
                 {"Row1.Left.IsLocked": true },
                 {"Row2.Right.IsLocked": true },
                 {"Row2.Left.IsLocked": true } ],
      "requestId": "5689"
    }

    receive <- {
        "action": "set",
        "requestId": "5689",
        "timestamp": 1489985044000
    }
	

Unsuccessful set. The value cannot be set.

    client -> {
        "action": "set",
        "path": "Signal.Drivetrain.InternalCombustionEngine.RPM",
        "value": 2000,
        "requestId": "8912"
    }

    receive <- {
        "action": "set",
        "requestId": "8912",
        "error": { "number": 401,
        "reason": "read_only",
        "message": "The desired signal cannot be set since it is a read only signal"},
        "timestamp": 1489985044000
    }
	

Unsuccessful set. The value does not exist in the specified path.

    client -> {
    "action": "set",
    "path": "Signal.Drivetrain.InternalCombustionEngine.RPM",
    "value": { "locked" : true }
    "requestId": "2311"
    }

    receive <- {
        "action": "set",
        "requestId": "2311",
        "error": { "number": 400,
        "reason": "bad_request" ,
        "message": "The server is unable to fulfill the client
                    request because the request is malformed."},
        "timestamp": 1489985044000
     }
	

Subscribe

Vehicle data subscriptions can provide data to the client whenever a signal changes on the server, unless otherwise specified using Server Side Filtering. The server MAY reduce the number of notifications sent to the client in order to reduce processing demands, particularly when the client has subscribed to continuously varying signals.

When the client makes a request to the server to create a new subscription, a JSON data object is returned. This object contains the attributes that were passed to the server to make the subscription and a subscriptionId integer handle value which is used to uniquely identify the subscription.

Subscribe Request

The subscribeRequest has the following properties and schema:

Object NameAttributeTypeRequired
subscribeRequest
actionActionYes
pathstringYes
filtersobjectNo
requestIdstringYes
{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Subscribe Request",
    "description": "Allows the client to subscribe to time-varying signal notifications on the server.",
    "type": "object",
    "required": ["action", "path", "requestId"],
    "properties": {
        "action": {
            "enum": [ "subscribe" ],
            "description": "The identifier for the subscription request"
        },
        "path": {
            "$ref": "#/definitions/path"
        },
        "filters": {
            "$ref": "#/definitions/filters"
        },
        "requestId": {
            "$ref": "#/definitions/requestId"
        }
    }
}

The subscribeSuccessResponse has the following properties and schema:

Object NameAttributeTypeRequired
subscribeSuccessResponse
actionActionYes
requestIdstringYes
subscriptionIdstringYes
timestampintegerYes
{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Subscribe Success Response",
    "description": "The response sent from the server upon a successful subscription request",
    "type": "object",
    "required": ["action", "requestId", "subscriptionId", "timestamp"],
    "properties": {
        "action": {
            "enum": [ "subscribe" ],
            "description": "The identifier for the subscription request"
        },
        "requestId": {
            "$ref": "#/definitions/requestId"
        },
        "subscriptionId": {
            "$ref": "#/definitions/subscriptionId"
        },                
        "timestamp": {
            "$ref": "#/definitions/timestamp"
        }
    }
}

The subscribeErrorResponse has the following properties and schema:

Object NameAttributeTypeRequired
subscribeErrorResponse
actionActionYes
requestIdstringYes
errorErrorYes
timestampintegerYes
{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Subscribe Error Response",
    "description": "The response sent from the server upon an unsuccessful subscribe request",
    "type": "object",
    "required": ["action", "requestId", "error", "timestamp"],
    "properties": {
        "action": {
            "enum": [ "subscribe" ],
            "description": "The identifier for the subscription request"
        },
        "requestId": {
            "$ref": "#/definitions/requestId"
        },
        "error": {
            "$ref": "#/definitions/error"
        },
        "timestamp": {
            "$ref": "#/definitions/timestamp"
        }
    }
}

The subscriptionNotification has the following properties and schema:

Object NameAttributeTypeRequired
subscriptionNotification
actionActionYes
subscriptionIdstringYes
valueanyYes
timestampintegerYes
{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Subscription Notification",
    "description": "Notification sent from the server to provide the requested data to the client",
    "type": "object",
    "required": ["action", "subscriptionId", "value", "timestamp"],
    "properties": {
        "action": {
            "enum": [ "subscription" ],
            "description": "The identifier for the subscription notification"
        },
        "subscriptionId": {
            "$ref": "#/definitions/subscriptionId"
        },
        "value": {
            "$ref": "#/definitions/value"
        },
        "timestamp": {
            "$ref": "#/definitions/timestamp"
        }
    }
}

The subscriptionNotificationError has the following properties and schema:

Object NameAttributeTypeRequired
subscriptionNotificationError
actionActionYes
subscriptionIdstringYes
errorErrorYes
timestampintegerYes
{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Subscription Notification Error",
    "description": "Error message sent by the server when there is an error with an existing subscription",
    "type": "object",
    "required": ["action", "subscriptionId", "error", "timestamp"],
    "properties": {
        "action": {
            "enum": [ "subscription" ],
            "description": "The identifier for the subscription notification"
        },
        "subscriptionId": {
            "$ref": "#/definitions/subscriptionId"
        },
        "error": {
            "$ref": "#/definitions/error"
        },
        "timestamp": {
            "$ref": "#/definitions/timestamp"
        }
    }
}

Examples

For example when a client wishes to subscribe to receive "Signal.Drivetrain.Transmission.TripMeter" information from the server, it can use the subscribe action with the target path property set as follows:

	client -> {
		"action": "subscribe",
		"path": "Signal.Drivetrain.Transmission.TripMeter",
		"requestId": "1004"
	}

	receive <- {
            "action": "subscribe",
            "requestId": "1004",
            "subscriptionId": "35472",
            "timestamp": 1489985044000
	}
  

This example is for illustrative purposes only, it MAY or MAY NOT be the case that the version of the Vehicle Server Specification implemented by the VIS Server includes the example path 'Signal.Drivetrain.Transmission.TripMeter'.

If the subscribeRequest is successful the server will return a subscribeSuccessResponse as shown in the example above, and the client will then start to receive subscription notifications like the one illustrated in the following JSON structure:

      receive <- {
          "action": "subscription",
          "subscriptionId": "35472",
          "value": 36912,
          "timestamp": 1489985044000
      }
  

The subscriptionId value is a unique value, created by the server and which may be used internally by the server to manage subscriptions on that WebSocket instance.

The subscriptionId value may also be used by the client to unsubscribe from receiving future notifications, by passing the value to the server with the unsubscribe action.

To differentiate subscription response from responses for ‘GET’ requests, subscription responses shall additionally include the subscriptionId value that identifies the subscription that triggered that notification.

The server ensures that a new unique subscriptionId value is returned for each successful subscription request on a particular WebSocket connection. However, the server does not guarantee that subscription handle values are unique between different WebSocket instances.

Once the subscription is successfully registered with the server, the client MAY receive subscription notifications containing the requested data. The notification rate MAY be specified using a server side filter. If there is an error with an existing subscription a subscriptionNotificationError SHALL be sent to the client. This allows the client to handle the error, for example by modifying the filter condition to reduce the requested notification frequency.

If the authentication token expires whilst a subscription is active, the server will send a subscriptionNotificationError for each subscription that can no longer be fulfilled. The client is then responsible for communicating with the relevant Security Authority to renew the authentication token.

After the client authentication token has expired, the server MAY continue to send notifications for data that is not subject to access control restrictions. The client will NOT receive notifications for subscriptions that require a valid authentication token until the client sends a renewed authentication token to the server.

If a new authentication token is presented by the client to the server in a timely manner, all existing subscriptions will continue to cause notifications to be sent from the server, provided the security principal(s) have not changed. The time window during which the client MAY re-authenticate and subscriptions continue WILL be implementation dependent. If the server has terminated any subscriptions, it is the responsibility of the client to renew them.

At any time the server MUST only send subscription notifications for data that the client is authorized to access.

The server MAY close the WebSocket connection at any time and in so doing, terminate all subscriptions for the client. The client is then responsible for renewing subscriptions. The client MUST close the WebSocket connection if the server data is no longer required.

An example of a subscription can be found here.

Unsubscribe

To unsubscribe from a subscription, the client SHALL send an unsubscribeRequest message to the server. This is comprised of a JSON structure which contains an action property set to unsubscribe and a string containing the subscriptionId. If the server is able to satisfy the request it returns an unsubscribeSuccessResponse. If an error occurs, for example because an invalid subscriptionId is passed to the server, an unsubscribeErrorResponse is returned.

If the client has created more than one WebSocket instance, it MUST always unsubscribe using the same WebSocket instance that was originally used to create the subscription.

It is not possible to unsubscribe from a subset of signals within a subscription. The client must unsubscribe and set up a new subscription to receive notifications for the desired signals. For example, if there is an active subscription for all lights, using the path Signal.Body.Lights.*, and the client requires information for only Signal.Body.Lights.IsLowBeamOn, the client will have to unsubscribe from the Signal.Body.Lights.* subscription and re-subscribe to Signal.Body.Lights.IsLowBeamOn.

The client should always unsubscribe from receiving notifications when it is no longer using the data. Over a long vehicle journey, this significantly reduces the processing load on the server and allow the server to free memory. It therefore makes it more likely that the server will remain responsive to future requests from the client.

Unsubscribe Request

The unsubscribeRequest has the following properties and schema:

Object NameAttributeTypeRequired
unsubscribeRequest
actionActionYes
subscriptionIdstringYes
requestIdstringYes
{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Unsubscribe Request",
    "description": "Allows the client to unsubscribe to time-varying signal notifications on the server.",
    "type": "object",
    "required": ["action", "subscriptionId", "requestId"],
    "properties": {
        "action": {
            "enum": [ "unsubscribe" ],
            "description": "The identifier for the unsubscribe request"
        },
        "subscriptionId": {
            "$ref": "#/definitions/subscriptionId"
        }, 
        "requestId": {
            "$ref": "#/definitions/requestId"
        }
    }
}

The unsubscribeSuccessResponse has the following properties and schema:

Object NameAttributeTypeRequired
unsubscribeSuccessResponse
actionActionYes
subscriptionIdstringYes
requestIdstringYes
timestampintegerYes
{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Unsubscribe Success Response",
    "description": "The response sent from the server upon a successful unsubscribe request",
    "type": "object",
    "required": ["action", "subscriptionId", "requestId", "timestamp"],
    "properties": {
        "action": {
            "enum": [ "unsubscribe" ],
            "description": "The identifier for the unsubscribe request"
        },
        "subscriptionId": {
            "$ref": "#/definitions/subscriptionId"
        }, 
        "requestId": {
            "$ref": "#/definitions/requestId"
        },
        "timestamp": {
            "$ref": "#/definitions/timestamp"
        }
    }
}

The unsubscribeErrorResponse has the following properties and schema:

Object NameAttributeTypeRequired
unsubscribeErrorResponse
actionActionYes
subscriptionIdstringYes
requestIdstringYes
errorErrorYes
timestampintegerYes
{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Unsubscribe Error Response",
    "description": "The response sent from the server upon an unsuccessful unsubscribe request",
    "type": "object",
    "required": ["action", "subscriptionId", "requestId", "error", "timestamp"],
    "properties": {
        "action": {
            "enum": [ "unsubscribe" ],
            "description": "The identifier for the subscription request"
        },
        "subscriptionId": {
            "$ref": "#/definitions/subscriptionId"
        }, 
        "requestId": {
            "$ref": "#/definitions/requestId"
        },
        "error": {
            "$ref": "#/definitions/error"
        },
        "timestamp": {
            "$ref": "#/definitions/timestamp"
        }
    }
}

Examples

The example below shows the JSON message structure for an unsubscribeRequest and an unsubscribeSuccessResponse which are used when unsubscribing from a single subscription.
	client -> {
		"action": "unsubscribe",
		"subscriptionId": "102",
		"requestId": "5264"
	}

	receive <- {
		"action": "unsubscribe",
		"subscriptionId": "102",
		"requestId": "5264",
		"timestamp": 1489985044000
	}
	
The following shows an example of error case with invalid subscriptionId.
	client -> {
		"action": "unsubscribe",
		"subscriptionId": "3542",
		"requestId": "7846"
	}
	receive <- {
		"action": "unsubscribe",
		"subscriptionId": "3542",
		"requestId": "7846",
		"error": { "number":404,
			"reason": "invalid_subscriptionId",
			"message": "The specified subscription was not found." },
		"timestamp": 1489985044000
	}
	
		// send unsubscribe message
		vehicle.send('{ "action": "unsubscribe", "subscriptionId": "102", "requestId": "5429" }');

		// set handler
		vehicle.onmessage(function(event){
			var msg = JSON.parse(event.data);
			// success case
			if(msg.hasOwnProperty("requestId") && msg.requestId == "5429"){
				console.log("Successfully unsubscribed for id " + msg.subscriptionId);
			// error case
			} else if (msg.hasOwnProperty("error")) {
				console.log("Unsuccessful unsubscribe. " + msg.error.message)
			}
		});
	

Unsubscribe All

To unsubscribe from all subscriptions, the client SHALL send an unsubscribeAllRequest message to the server. This is comprised of a JSON structure which contains an action property set to unsubscribeAll. This does not require a subscriptionId value. If the request is successful, or there are no active subscriptions to unsubscribe from, the VIS Server will return an unsubscribeSuccessResponse.

Unsubscribe All Request

The unsubscribeAllRequest has the following properties and schema:

Object NameAttributeTypeRequired
unsubscribeAllRequest
actionActionYes
requestIdstringYes
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "unsubscribeAll Request",
  "description": "Allows the client to unsubscribe from all notifications on the server.",
  "type": "object",
  "required": ["action", "requestId"],
  "properties": {
    "action": {
      "enum": [ "unsubscribeAll" ],
      "description": "The identifier for the unsubscribeAll request"
    },
    "requestId": {
      "$ref": "#/definitions/requestId"
    }
  }
}

The unsubscribeAllSuccessResponse has the following properties and schema:

Object NameAttributeTypeRequired
unsubscribeAllSuccessResponse
actionActionYes
requestIdstringYes
timestampintegerYes
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "unsubscribeAll Success Response",
  "description": "The response sent from the server upon a successful unsubscribeAll request",
  "type": "object",
  "required": ["action", "requestId", "timestamp"],
  "properties": {
    "action": {
      "enum": [ "unsubscribeAll" ],
      "description": "The identifier for the unsubscribeAll request"
    },
    "requestId": {
      "$ref": "#/definitions/requestId"
    },
    "timestamp": {
      "$ref": "#/definitions/timestamp"
    }
  }
}
unsubscribeAllErrorResponse

The unsubscribeAllErrorResponse has the following properties and schema:

Object NameAttributeTypeRequired
unsubscribeAllErrorResponse
actionActionYes
requestIdstringYes
errorErrorYes
timestampintegerYes
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "UnsubscribeAll Error Response",
  "description": "The response sent from the server upon an unsuccessful unsubscribeAll request",
  "type": "object",
  "required": ["action", "requestId", "error", "timestamp"],
  "properties": {
    "action": {
    "enum": [ "unsubscribeAll" ],
    "description": "The identifier for the unsubscribeAll request"
    },
    "requestId": {
      "$ref": "#/definitions/requestId"
    },
    "error": {
      "$ref": "#/definitions/error"
    },
    "timestamp": {
      "$ref": "#/definitions/timestamp"
    }
  }
}

Examples

The example below shows an unsubscribeAllRequest and an unsubscribeAllSuccessResponse for the unsubscribeAll action. This is used by the client to unsubscribe from all subscriptions created for that client.
client -> {
    "action": "unsubscribeAll",
    "requestId": "3468"
}

receive <- {
    "action": "unsubscribeAll",
    "requestId": "3468",
    "timestamp": 1489985044000
}

Server Side Filtering

Filters may be specified to enable Server side filtering to be used to throttle the demands of subscriptions on the server. This may enable the reduction of traffic if the developer has received a '429 - Too Many Requests' error message.

Server side filters are extensible, allowing for additional filtering mechanisms to be specified. Support is currently included for a specific range of values, time intervals and minimum changes. This can be implemented using the filters option in the subscribeRequest interface.

Filters can only be applied for data which is represented by a primitive type, such as leaf nodes in the VSS tree, not entire branches. For example, a filter cannot be set on Signal.Drivetrain.InternalCombustionEngine.*, however it can be set on Signal.Drivetrain.InternalCombustionEngine.RPM.

The default behaviour for a subscription is to request that the server sends a notification to the client 'onchange'. To modify this behaviour the client can use filtering options. The following filter options SHALL be supported:

The client can use filters to request that the server sends notifications based on various criteria, but it is important to note that this is just a request and the notification frequency will ultimately be determined by the server.

JSON Schema

{
  "filters": {
      "$ref": "#/definitions/filters"
  }
}

Examples

The following structures are examples of subscribeRequest objects which are requesting server side filters

	//client requests data every 100ms
	{ "action": "subscribe", "path": "<any_path>",
        	"filters": { "interval": 100 },
        	"requestId": "<some_unique_value>" }

	//client requests data when the value is between 100 and 200 (inclusive)
	{ "action": "subscribe", "path": "<any_path>",
        	"filters": { "range": { "above": 100, "below": 200 } },
        	"requestId": "<some_unique_value>" }

	//client requests data when the value is below 100 (inclusive)
	{ "action": "subscribe", "path": "<any_path>",
        	"filters": { "range": { "below": 100 } },
        	"requestId": "<some_unique_value>" }

	//client requests data when the value changes by 100 units
	{ "action": "subscribe", "path": "<any_path>",
        	"filters": { "minChange": 100 },
        	"requestId": "<some_unique_value>" }

	//client requests data when the value is above 200 (inclusive)
	//and the value changes by at least 20 units
	{ "action": "subscribe", "path": "<any_path>",
        	"filters": { "range": { "below": 200 }, "minChange": 20},
        	"requestId": "<some_unique_value>" }
      

In order to prevent adding unnecessary load on the server, the client should not specify a minimum change amount that is smaller than it needs. The server SHALL return a '429 - Too Many Request' error response if it is unable to fulfill the request made by the client because it is processing too many requests.

WebSocket Closure

The WebSocket may be closed by either the client or the server by invoking the ‘close()’ method on the WebSocket instance.

The following example shows the lifetime of a WebSocket on the client:

// Open the WebSocket
var vehicle  = new WebSocket("wss://localhost:4343", "wvss1.0");

// WebSocket is used to GET, SET, SUBSCRIBE and UNSUBSCRIBE
…

// Close the WebSocket
vehicle.close();
	

The VIS Server may terminate the WebSocket connection if it has not received a request for a period determined by the server. It is the client’s responsibility to handle this gracefully and to recover and request new subscriptions, where required.

The section that follows defines the error responses that shall be supported by the server.

Errors

If there is an error with any of the client’s requests, the server responds with an error number, reason and message.

The Error object has the following properties and schema:

Object NameAttributeTypeRequired
Error
numberintegerYes
reasonstringYes
messagestringYes
{
  "error": {
      "$ref": "#/definitions/error"
  }
}

Examples

For some error codes, for example '401 (Unauthorized)' there can be more than one cause. The error number that is returned is the HTTP Status Code Number e.g. 401. An error reason is also returned, this contains a pre-defined string value that can be used to distinguish between errors that have the same code (e.g. '401 Unauthorized)' but a difference cause. The error message is used to provide message text describing the cause in more detail.

      client -> { 
        "action": "subscribe",
      	"filters": { "<filter_expression>" },
      	"path": "<any_metadata_definition>",
      	"requestId": "<some_unique_value>" 
      }
      receive on error <- {
          "action": "subscribe",
          "requestId": "<some_unique_value>",
          "error":{ 
            "number": "<error_num>",
            "reason": "<error_reason>",
            "message": "<error_message>" 
          },
          "timestamp": "1489985044000" 
        }
	

The server implementation SHALL support at least the error numbers and reasons listed in the table below.

Error Number (Code) Error Reason Error Message
304 (Not Modified) not_modified No changes have been made by the server.
400 (Bad Request) bad_request The server is unable to fulfill the client request because the request is malformed.
400 (Bad Request) filter_invalid Filter requested on non-primitive type.
401 (Unauthorized) user_token_expired User token has expired.
401 (Unauthorized) user_token_invalid User token is invalid.
401 (Unauthorized) user_token_missing User token is missing.
401 (Unauthorized) device_token_expired Device token has expired.
401 (Unauthorized) device_token_invalid Device token is invalid.
401 (Unauthorized) device_token_missing Device token is missing.
401 (Unauthorized) too_many_attempts The client has failed to authenticate too many times.
401 (Unauthorized) read_only The desired signal cannot be set since it is a read only signal.
403 (Forbidden) user_forbidden The user is not permitted to access the requested resource. Retrying does not help.
403 (Forbidden) user_unknown The user is unknown. Retrying does not help.
403 (Forbidden) device_forbidden The device is not permitted to access the requested resource. Retrying does not help.
403 (Forbidden) device_unknown The device is unknown. Retrying does not help.
404 (Not Found) invalid_path The specified data path does not exist.
404 (Not Found) private_path The specified data path is private and the request is not authorized to access signals on this path.
404 (Not Found) invalid_subscriptionId The specified subscription was not found.
406 (Not Acceptable) not_acceptable The server is unable to generate content that is acceptable to the client
429 (Too Many Requests) too_many_requests The client has sent the server too many requests in a given amount of time.
502 (Bad Gateway) bad_gateway The server was acting as a gateway or proxy and received an invalid response from an upstream server.
503 (Service Unavailable) service_unavailable The server is currently unable to handle the request due to a temporary overload or scheduled maintenance (which may be alleviated after some delay).
504 (Gateway Timeout) gateway_timeout The server did not receive a timely response from an upstream server it needed to access in order to complete the request.

The server may optionally return additional error codes. It is expected that if this is the case, they are defined in the Server Documentation. Wherever possible the Server returns a standard HTTP error code where one has been defined for the error condition. See for example RFC7231, RFC7235, and RFC6585.