Updated on 2025-07-29 GMT+08:00

MQTT(S) Codec Example

This section uses a smoke detector as an example to describe how to develop a FunctionGraph codec in JavaScript for reporting properties and delivering commands over MQTT or MQTTS. The codec converts binary data into JSON data and provides a method for debugging.

Defining a Smoke Detector

Scenario

A smoke detector provides the following functions:

  • Reporting smoke alarms (fire severity) and temperature.
  • Receiving and running remote control commands, which can be used to enable the alarm function remotely. For example, the smoke detector can report the temperature on the fire scene and remotely trigger a smoke alarm for evacuation.
  • The smoke detector has weak capabilities and cannot report data in JSON format defined by the device APIs, but reporting simple binary data.

Product Model

Define the product model on the product details page of the smoke detector.
  • level: indicates the fire severity.
  • temperature: indicates the temperature at the fire scene.
  • SET_ALARM: indicates whether to enable or disable the alarm function. The value 0 indicates that the alarm function is disabled, and 1 indicates that the alarm function is enabled. The response command result is used to report the modified alarm value.
    Figure 1 Model definition - smokedetector

Developing a Codec

  1. On the smoke detector product page, click the Codec Development tab, select FunctionGraph, and click Create Function. If you use the tool for the first time, perform access authorization.

    Figure 2 FunctionGraph-based Development - Codec authorization

  2. On the FunctionGraph console, click Create Function. On the displayed page, click Create from scratch, enter a function name, and select Node.js 16.17 as the runtime.

    Figure 3 Function list - Creating a function
    Figure 4 Creating a function - Parameters

  3. Write a script to convert binary data into JSON data. The script must implement the following methods:

    • Decode: Converts the binary data reported by a device into the JSON format defined in the product model. For details about the JSON format requirements, see Data Decoding Format Definition.
    • Encode: Converts JSON data into binary data supported by the device when the platform sends downstream data to the device. For details about the JSON format requirements, see Data Encoding Format Definition.
    The following is an example of the JavaScript implemented for the smoke detector. Copy the code to the project and click the button for deploying the code.
    // Upstream message types
    var MSG_TYPE_PROPERTIES_REPORT = 'properties_report'; // Device property reporting
    var MSG_TYPE_COMMAND_RSP = 'command_response'; // Command response
    var MSG_TYPE_PROPERTIES_SET_RSP = 'properties_set_response'; // Property setting response
    var MSG_TYPE_PROPERTIES_GET_RSP = 'properties_get_response'; // Property query response
    var MSG_TYPE_MESSAGE_UP = 'message_up'; // Device message reporting
    // Downstream message types
    var MSG_TYPE_COMMANDS = 'commands'; // Command delivery
    var MSG_TYPE_PROPERTIES_SET = 'properties_set'; // Property setting request
    var MSG_TYPE_PROPERTIES_GET = 'properties_get'; // Property query request
    var MSG_TYPE_MESSAGE_DOWN = 'messages'; // Platform message delivery
    
    // Mapping between topics and upstream MQTT message types
    var TOPIC_REG_EXP = {
        'properties_report': new RegExp('\\$oc/devices/(\\S+)/sys/properties/report'),
        'properties_set_response': new RegExp('\\$oc/devices/(\\S+)/sys/properties/set/response/request_id=(\\S+)'),
        'properties_get_response': new RegExp('\\$oc/devices/(\\S+)/sys/properties/get/response/request_id=(\\S+)'),
        'command_response': new RegExp('\\$oc/devices/(\\S+)/sys/commands/response/request_id=(\\S+)'),
        'message_up': new RegExp('\\$oc/devices/(\\S+)/sys/messages/up')
    };
    exports.handler = async (event, context) => {
        const codecType = event.codecType;
        const message = JSON.parse(event.message);
        console.log("input Data:", event);
        if (codecType === "decode") {
            // Decoding operation
            return decode(message.payload, message.topic);
        } else if (codecType === "encode") {
            // Encoding operation
            return encode(message);
        }
    }
    /*
    Example: When a smoke detector reports properties and returns a command response, it uses binary code streams. The JavaScript script will decode the binary code streams into JSON data that complies with the product model definition.
    Input parameters:
      // The first two bytes 0x00 and 0x50 are the value of the level property, and the last two bytes 0x00 and 0x5a are the value of the temperature property.
      payload:[0x00, 0x50, 0x00, 0x5a] 
      topic:$oc/devices/cf40f3c4-7152-41c6-a201-a2333122054a/sys/properties/report
    Output:
      {"msg_type":"properties_report","services":[{"service_id":"smokerdector","properties":{"level":80,"temperature":90}}]}
    Input parameters:
      // The first byte 0x02 indicates that the command_name is SET_ALARM. The second byte 0x00 indicates that the command is successfully responded. The last two bytes 0x00 and 0x01 indicate the value of the command response.
      payload: [0x02, 0x00, 0x00, 0x01] 
      topic: $oc/devices/cf40f3c4-7152-41c6-a201-a2333122054a/sys/commands/response/request_id=bf40f0c4-4022-41c6-a201-c5133122054a
    Output:
      {"msg_type":"command_response","result_code":0,"command_name":"SET_ALARM","service_id":"smokerdector","paras":{"value":"1"}}
    */
    // Decoding function
    function decode(payload, topic) {
        // Decoding logic
        var binaryString = atob(payload);
        const byteArray = new Uint8Array(binaryString.length);
        for (let i = 0; i < binaryString.length; i++) {
            byteArray[i] = binaryString.charCodeAt(i);
        }
        /*  byteArray is the binary data reported by the device after decoding. You can check whether the reported data is correctly parsed.*/
        var returnData;
        msgType = topicParse(topic);
            if (msgType == MSG_TYPE_PROPERTIES_REPORT) {
            returnData = decodePropertiesReport(byteArray);
        } else if (msgType == MSG_TYPE_COMMAND_RSP) {
            returnData = decodeCommandRsp(byteArray);
        } else if (msgType == MSG_TYPE_PROPERTIES_SET_RSP) {
            // Convert data to the JSON format used by the property setting response.
            // jsonObj = {"msg_type":"properties_set_response","result_code":0,"result_desc":"success"};
            // returnData = outputData(status, jsonObj)
        } else if (msgType == MSG_TYPE_PROPERTIES_GET_RSP) {
            // Convert data to the JSON format used by the property query response.
            // jsonObj = {"msg_type":"properties_get_response","services":[{"service_id":"analog","properties":{"PhV_phsA":"1","PhV_phsB":"2"}}]};
            // returnData = outputData(status, jsonObj)
        } else if (msgType == MSG_TYPE_MESSAGE_UP) {
            // Convert the data to the JSON format used by message reporting.
            // jsonObj = {"msg_type":"message_up","content":"hello"};
            // returnData = outputData(status, jsonObj)
        }
        return returnData;
    }
    // Encoding function
    /*
    Sample data: When a command is delivered, data in JSON format on IoTDA is encoded into a binary code stream using the encode method of JavaScript.
    Input parameters ->
        {"msg_type":"commands","command_name":"SET_ALARM","service_id":"smokerdector","paras":{"value":1}}
    Output ->
        // The first byte 0x01 is used to identify command delivery. The second byte 0x00 indicates command_name = = 'SET_ALARM'. The last two bytes 0x00 and 0x01 are the value of the command properties.
        [0x01, 0x00, 0x00, 0x01] 
    */
    function encode(data) {
        var msgType = data.msg_type;
        let payload = [];
        var status = 200;
        // Command delivery
        if (msgType == MSG_TYPE_COMMANDS) {
            payload[0] = 0x02; // Command delivery type
            if (data.command_name == 'SET_ALARM') {
                payload[1] = 0x00; // Command name
            }
            // Set the command property value
            payload[2] = (data.paras.value >> 8) & 0xFF;
            payload[3] = data.paras.value & 0xFF;
        } else if (msgType == MSG_TYPE_PROPERTIES_SET) {
            // Response to device property reporting
            // Property setting format example: {"msg_type":"properties_set","services":[{"service_id":"Temperature","properties":{"value":57}}]}
            // Convert the JSON format to the corresponding binary code streams if the property setting scenario is involved.
        } else if (msgType == MSG_TYPE_PROPERTIES_GET) {
            // Property query format example: {"msg_type":"properties_get","service_id":"Temperature"}
            // Convert the JSON format to the corresponding binary code streams if the property query scenario is involved.
        } else if (msgType == MSG_TYPE_MESSAGE_DOWN) {
            // Message delivery format example: {"msg_type":"messages","content":"hello"}
            // Convert the JSON format to the corresponding binary code streams if the message delivery scenario is involved.
        }
        return outputData(status, { "payload": payload });
    }
    // Parse the message type based on the topic name.
    function topicParse(topic) {
        for (var type in TOPIC_REG_EXP) {
            var pattern = TOPIC_REG_EXP[type];
            if (pattern.test(topic)) {
                return type;
            }
        }
        return '';
    }
    // Property reporting (upstream)
    function decodePropertiesReport(byteArray) {
        // Set the value of serviceId, which corresponds to smokerdector in the product model.
        var serviceId = 'smokerdector';
        var level = byteArray[0] * Math.pow(2, 8) + byteArray[1];
        var status = 200;
        var jsonObj;
        if (byteArray.length < 4) {
            jsonObj = {
                "msg_type": "ERR", "message": "decodePropertiesReport byte length < 5."
            };
            status = 402;
        }
        // Obtain the values of the fourth and fifth values.
        const integerPart = byteArray[2]; // Third value
        const decimalPart = byteArray[3]; // Fourth value
        // Combine the values into decimals.
        const temperature = parseFloat(integerPart + '.' + decimalPart);
        jsonObj = {
            "msg_type": MSG_TYPE_PROPERTIES_REPORT, "services":
                [{ "service_id": serviceId, "properties": { "level": level, "temperature": temperature } }]
        };
        return outputData(status, jsonObj);
    }
    // Command response (upstream)
    function decodeCommandRsp(byteArray) {
        var serviceId = 'smokerdector';
        var command = byteArray[0];
        var command_name = '';
        if (2 == command) {
            command_name = 'SET_ALARM';
        }
        var result_code = byteArray[1]; // Obtain the command execution result from the binary code stream.
        var value = byteArray[2] * Math.pow(2, 8) + byteArray[3]; // Obtain the return value of the command execution result from the binary code stream.
        // Convert data into the JSON format used by the command response.
        jsonObj = {
            'msg_type': MSG_TYPE_COMMAND_RSP, 'service_id': serviceId, "command_name": command_name,
            'result_code': result_code, 'paras': { 'value': value }
        };
        return outputData(200, jsonObj);
    }
    // Output the result.
    function outputData(status, body) {
        const output =
        {
            'status': status,
            'message': JSON.stringify(body),
        }
        console.log("output Data:", output);
        return output;
    } 
    Figure 5 FunctionGraph - Copying code to a project

  4. Debug the script online. After the script is edited, click Configure Test Event on the FunctionGraph console, select a blank template, enter simulated data, and click Create. After configuring the test event, click Test to obtain the function result and logs.

    Simulated data: payload is the binary data reported by the device, that is, 0x01, 0x02, 0x03, 0x04. AQIDBA== is the result value encoded by the platform using Base64.
    {
        "codecType": "decode",
        "message": "{\"topic\": \"$oc/devices/device_id/sys/properties/report\",\"payload\": \"AQIDBA==\"}"
    }
    Figure 6 FunctionGraph - Adding a test event
    Figure 7 FunctionGraph - Test result (MQTT)

  5. After the debugging is successful, select the created FunctionGraph function from the drop-down list in 1 and click Deploy.

    Figure 8 FunctionGraph-based Development - Codec deployment