This article provides the script template and a sample script for parsing Thing Specification Language (TSL) data in JavaScript.

Script template

You can write a script in JavaScript based on the following script template to parse TSL data:

Note This template applies only to products whose Data Format parameter is set to Custom.
/**
 * Call the protocolToRawData function to convert Alink data to data that can be recognized by a device when IoT Platform sends data to the device.
 * jsonObj: an input parameter. The value is a JSON object that cannot be left empty.
 * rawData: an output parameter. The value is a byte array that cannot be left empty.
 *
 */
function protocolToRawData(jsonObj) {
    return rawdata;
}

/**
 * Call the rawDataToProtocol function to convert custom data of a device to Alink data when the device reports data to IoT Platform.
 * rawData: an input parameter. The value is a byte array that cannot be left empty.
 * jsonObj: an output parameter. The value is a JSON object that cannot be left empty.
 */
function rawDataToProtocol(rawData) {
    return jsonObj;
}
            

Notes for script writing

  • Do not use global variables. Otherwise, results may be inconsistent.
  • The script is used to process data by using the two's complement operation. The complement range for values in the range of [-128,127] is [0,255]. For example, the complement of -1 is 255 in decimal.
  • The rawDataToProtocol function is used to parse the data that is reported by devices. The input of the function is an integer array. Use 0xFF for the bitwise AND operation to obtain the complement.
  • The protocolToRawData function parses the data that is sent by IoT Platform and returns the result in an array. Each element in the array must be an integer within a value range of [0,255].

Sample script

The following sample script is written based on the properties and protocol that are defined in the Example for parsing TSL data article:

var COMMAND_REPORT = 0x00 // Property data reported by devices.
var COMMAND_SET = 0x01 // Property setting command data from the cloud.
var COMMAND_REPORT_REPLY = 0x02  // Response data for devices reporting properties.
var COMMAND_SET_REPLY = 0x03  // Response data for setting device properties.
var Command_unkown = 0xff  // Unknown commands.
var ALINK_PROP_REPORT_METHOD = 'thing.event.property.post' // The topic for devices to upload property data to the cloud.
var ALINK_PROP_SET_METHOD = 'thing.service.property.set'  // The topic for IoT Platform to send a property control command to devices.
var ALINK_PROP_SET_REPLY_METHOD = 'thing.service.property.set'  // The topic for devices to report the property setting results to IoT Platform.
var SELF_DEFINE_TOPIC_UPDATE_FLAG = '/user/update'  // Custom topic: /user/update.
var SELF_DEFINE_TOPIC_ERROR_FLAG = '/user/update/error' // Custom topic: /user/update/error.
/*
Sample data:
The device reports property data.
Input parameters:
    0x000000000100320100000000
Output result:
    {"method":"thing.event.property.post","id":"1","params":{"prop_float":0,"prop_int16":50,"prop_bool":1},"version":"1.0"}

The device responds after setting properties.
Input parameters:
    0x0300223344c8
Output result:
    {"code":"200","data":{},"id":"2241348","version":"1.0"}
*/
function rawDataToProtocol(bytes) {
    var uint8Array = new Uint8Array(bytes.length);
    for (var i = 0; i < bytes.length; i++) {
        uint8Array[i] = bytes[i] & 0xff;
    }
    var dataView = new DataView(uint8Array.buffer, 0);
    var jsonMap = new Object();
    var fHead = uint8Array[0]; // The command prefix.
    if (fHead == COMMAND_REPORT) {
        jsonMap['method'] = ALINK_PROP_REPORT_METHOD; // The topic for reporting properties in the ALink JSON.
        jsonMap['version'] = '1.0'; // The fixed protocol version in the ALink JSON.
        jsonMap['id'] = ''+ dataView.getInt32(1); // The request ID in the ALink JSON.
        var params = {};
        params['prop_int16'] = dataView.getInt16(5); // The value of the prop_int16 property.
        params['prop_bool'] = uint8Array[7]; // The value of the prop_bool property.
        params['prop_float'] = dataView.getFloat32(8); // The value of the prop_float property.
        jsonMap['params'] = params; // The params field in the ALink JSON.
    } else if(fHead == COMMAND_SET_REPLY) {
        jsonMap['version'] = '1.0'; // The fixed protocol version in the ALink JSON.
        jsonMap['id'] = ''+ dataView.getInt32(1); // The request ID in the ALink JSON.
        jsonMap['code'] = ''+ dataView.getUint8(5);
        jsonMap['data'] = {};
    }

    return jsonMap;
}
/*
Sample data:
IoT Platform pushes a command for setting properties to the device.
Input parameters:
    {"method":"thing.service.property.set","id":"12345","version":"1.0","params":{"prop_float":123.452, "prop_int16":333, "prop_bool":1}}
Output result:
    0x0100003039014d0142f6e76d

IoT Platform responds after the device reports properties.
Input data:
    {"method":"thing.event.property.post","id":"12345","version":"1.0","code":200,"data":{}}
Output result:
    0x0200003039c8
*/
function protocolToRawData(json) {
    var method = json['method'];
    var id = json['id'];
    var version = json['version'];
    var payloadArray = [];
    if (method == ALINK_PROP_SET_METHOD) // Sets properties
    {
        var params = json['params'];
        var prop_float = params['prop_float'];
        var prop_int16 = params['prop_int16'];
        var prop_bool = params['prop_bool'];
        // Raw data is concatenated based on the custom protocol format.
        payloadArray = payloadArray.concat(buffer_uint8(COMMAND_SET)); // The command field.
        payloadArray = payloadArray.concat(buffer_int32(parseInt(id))); // The id in the ALink JSON.
        payloadArray = payloadArray.concat(buffer_int16(prop_int16)); // The value of the prop_int16 property.
        payloadArray = payloadArray.concat(buffer_uint8(prop_bool)); // The value of the prop_bool property.
        payloadArray = payloadArray.concat(buffer_float32(prop_float)); // The value of the prop_float property.
    } else if (method == ALINK_PROP_REPORT_METHOD) {// The response of the reported data.
        var code = json['code'];
        payloadArray = payloadArray.concat(buffer_uint8(COMMAND_SET)); // The command field.
        payloadArray = payloadArray.concat(buffer_int32(parseInt(id))); // The id in the ALink JSON.
        payloadArray = payloadArray.concat(buffer_uint8(code));
    } Else {// Unknown commands. Some commands are not processed.
        var code = json['code'];
        payloadArray = payloadArray.concat(buffer_uint8(COMMAND_UNKOWN)); // The command field.
        payloadArray = payloadArray.concat(buffer_int32(parseInt(id))); // The id in the ALink JSON.
        payloadArray = payloadArray.concat(buffer_uint8(code));
    }
    return payloadArray;
}

/*
  Sample data
  Custom topic for reporting data: /user/update
  Input parameters: 
     topic: /{productKey}/{deviceName}/user/update
     bytes: 0x000000000100320100000000
  Output parameters:
  {
     "prop_float": 0,
     "prop_int16": 50,
     "prop_bool": 1,
     "topic": "/{productKey}/{deviceName}/user/update"
   }
 */
function transformPayload(topic, bytes) {
    var uint8Array = new Uint8Array(bytes.length);
    for (int i = 0; i < edges.length; i++) {
        uint8Array[i] = bytes[i] & 0xff;
    }
    var dataView = new DataView(uint8Array.buffer, 0);
    var jsonMap = {};

    if(topic.includes(SELF_DEFINE_TOPIC_ERROR_FLAG)) {
        jsonMap['topic'] = topic;
        jsonMap['errorCode'] = dataView.getInt8(0)
    } else if (topic.includes(SELF_DEFINE_TOPIC_UPDATE_FLAG)) {
        jsonMap['topic'] = topic;
        jsonMap['prop_int16'] = dataView.getInt16(5);
        jsonMap['prop_bool'] = uint8Array[7];
        jsonMap['prop_float'] = dataView.getFloat32(8);
    }

    return jsonMap;
}

// The following are some helper functions.
function buffer_uint8(value) {
    var uint8Array = new Uint8Array(1);
    var dv = new DataView(uint8Array.buffer, 0);
    dv.setUint8(0, value);
    return [].slice.call(uint8Array);
}
function buffer_int16(value) {
    var uint8Array = new Uint8Array(2);
    var dv = new DataView(uint8Array.buffer, 0);
    dv.setInt16(0, value);
    return [].slice.call(uint8Array);
}
function buffer_int32(value) {
    var uint8Array = new Uint8Array(4);
    var dv = new DataView(uint8Array.buffer, 0);
    dv.setInt32(0, value);
    return [].slice.call(uint8Array);
}
function buffer_float32(value) {
    var uint8Array = new Uint8Array(4);
    var dv = new DataView(uint8Array.buffer, 0);
    dv.setFloat32(0, value);
    return [].slice.call(uint8Array);
}