Project

General

Profile

Actions

Javascript guide for DTXr code IDE

Shows the various commands and events that can be used to build automation scripts.

Commands
Events

NOTE, the below spec is 100% compatible with version 3.1.13. Ask DAVITOR for compatibility with previous versions or update your device.


Commands

    //These two variable is used inside scratchPad function below for convinience.
    //Set gThisDeviceId to what you have set (or got) for bacnet device id in the network!
    //Set gRemoteDeviceId to any other device id on the network.

    var gThisDeviceId = 123456;   
    var gRemoteDeviceId = 121212;   

    /** LOG/DEBUG **/

    //Prints to console log window as well as autmation.log
    //Note that automation.log is also written to by various services in DTX by
    //default, not only what you do with print() in your code.
    //Consider this as a volatile log where you debug and then clear from time to time.
    //10Mbyte is max then it will rollover to time-stamped zip file and automation.log is cleared.
    print("hello");

    //Prints to automation_user.log
    //This statement is the only source for loggin in this file.
    //Consider this to be a persistant log of important process matters.
    //10Mbyte is max then it will rollover to time-stamped zip file anf automation-user.log is cleared.
    Controller.printToUserLog("hello user log");

    /** BACnet related **/

    //Translation helper for BACnet object ID->NAME
    //Many even arguments are enumerated where theese function can help
    print(getObjectTypeById(4)); //-> "binaryOutput" 
    print(getPropertyNameById(85)); //-> "presentValue" 

    // *** Write a value to an object property ***
    // As you can see there is no syntax difference writing to local or remote objects.

    //Local object
    Controller.writeProperty(gThisDeviceId,analogValue,0,presentValue,priority_1,active);
    //Remote object
    Controller.writeProperty(gRemoteDeviceId,analogValue,0,presentValue,priority_1,active);

    // *** Read a value from a property. *** 
    //Will return both primitiv and constructed (JSON) values

    //Primitive
    print(Controller.readProperty(gThisDeviceId,analogValue,0,presentValue));
    print(Controller.readProperty(gRemoteDeviceId,analogValue,0,presentValue));

    //Constructed (always JSON format from complex values)
    //A priorityArray property is one example of constructed result
    print(Controller.readProperty(gThisDeviceId,analogValue,0,statusFlags));

    // *** COV Subscribtion ***

    //With auto incremented process id, sensitivity (increment) of 1 (analog values) and subscription lifetime of 300s
    Controller.COVSubscribe(covProcIncr,gRemoteDeviceId,analogValue,instance_0,noCovConfirmation,presentValue,1 /*sensitivity*/,300 /*lifetime (s)*/);

    //With fixed process id 123, sensitivity (increment) no sesnitivity (binary values) and subscription lifetime of 300s
    Controller.COVSubscribe(123,gRemoteDeviceId,analogValue,instance_0,noCovConfirmation,presentValue,null,300 /*lifetime (s)*/);

    //UN-Subscribe with specific process id 10
    Controller.COVSubscribe(123,gRemoteDeviceId,analogValue,instance_0,null,presentValue,defaultIncrement,null);

    // *** Intrinsic Reporting (Alarms and Events) ***

    //Remote Event Subscribe
    Controller.eventSubscribe(gRemoteDeviceId/*device id*/,instance_0/*Notification Class*/);

    //Enable event/alarm reporting on object
    Controller.enableIntrinsicReporting(0/*NotificationClass*/,3/*delay*/,3/*delayNormal*/,analogValue,instance_0,notifyTypeEvent);

    Controller.writeProperty(analogValue,instance_0,presentValue,priority_1,50);
    //TextMessage
    Controller.sendTextMessage(gThisDeviceId,"SOME_TYPE_OF_MSG","hello myself, what's up?");

    //Acknowledge alarm and events
    Controller.acknowledgeAlarm(gThisDeviceId,analogValue,instance_0,processIdZero,
                        ackNormal,1584383651150,"Toca",new Date().getTime());

    //Issue an alert for a specific object via an Alert Enrollment Object
    //The recipients in the notification class connected to the shosen alert enrollment object will receive the alert
    var alertEnrollmentObjectInstance = 0;
    var propretaryEventType = 666;
    Controller.issueAlert(alertEnrollmentObjectInstance,analogValue,0,"To high!",propretaryEventType);     

    Controller.getEnrollmentSummary(gThisDeviceId);

    //Send event notif local or remote nodes
    //DeviceId,NotificationClass,AckRequired,Message
    Controller.sendNotification(gThisDeviceId,0,1,"Coffe anyone?");

    //Get all alarms for a specific device. Return JSON
    resp =  print(Controller.getAlarmSummary(gThisDeviceId));

    //Get all events for a specific device. Return JSON
    resp =  print(Controller.getEventSummary(gThisDeviceId));

    // *** Special Object Types *** //

    //Read a range of datapoints from a TrendLog object
    //The response is JSON with an array of value/timestamp
    Controller.readRange(gThisDeviceId,trendLogMultiple,0);

    // *** HTTP REST and Web Socket ***

    //HTTP GET Request. Returns respons as string
    resp = Controller.HTTP_GET('https://httpbin.org','get','Accept:application/json|Content-Type:application/json','myparam=hello');

    //HTTP POST(also PUT and PATCH) Request. Return response as string
    resp = Controller.HTTP_POST('https://httpbin.org/post'
                ,'Accept:application/json|Content-Type:application/json'
                ,'myparam=hello'
                ,'any payload string data');  

    //Web socket call to any webpage in the project file
    //This require that the page has loaded the Ws.js import.
    //Se HTTP Websocket template from the project tree sub menu
    Controller.sendToWS("mypage","Hello My Page this is a payload"); 

    //Connect to a Web Socket
    //DTX has a built in single web socket client.
    //Connect
    Controller.connectWebSocket("wss://any.websocket.host");
    //Send a message once connection is established
    Controller.sendWebSocketText("Hello");

    // *** SQL relational database access ***

    //Note, SQL db is not embedded so JDBC config is made in settings in advance.
    //Only PostgresSQL is supported for the moment!

    //Simple query. Result (if any) will always be JSON!
    print(Controller.sqlExecuteQuery("select * from anytable"));

    //Inserts and updates are preferably solved with functions or procedures on
    //the databas side. The CALL statement can then be utilized:
    Controller.sqlExecuteQuery("CALL upsert_anytable("+name+",'"+age+"')");

    //But of course a simple insert can also be sent...
    print(Controller.sqlExecuteQuery("insert into anytable values('kalle','13')"));

    // *** Timers and Schedulers ***

    //Show all current jobs (including system jobs)
    Controller.schedulerSummary();

    //Pause all jobs
    Controller.pauseAllJobs();

    //Pause a specific job
    Controller.pauseJob("JobA");

    //Resume all jobs
    Controller.resumeAllJobs();

    //Resume a specific job
    Controller.resumeJob("JobA");

    //Start job
    //Eg. executes function myCallbackFunction() with argument "df" after 10 seconds
    Controller.startJob('Job1',10,'myCallbackFunction("df")');
    //Eg. executes function myCallbackFunction() repeatedly every minute
    //with a start delay of 5 seconds
    Controller.startJob('Job2',5,60,'myCallbackFunction');

    //Eg. start a CRON job that executes myCallbackFunction
    //at 00:10 AM (10 minute past midnight) every day
    Controller.startCronJob("Job3",'0 10 0 ? * * *','myCallbackFunction');
    //Note: CRON can be difficult to construct. 
    //Use the below link to both crete and also verify your CRON strings.
    //https://www.freeformatter.com/cron-expression-generator-quartz.html

    //Cancel a job by using the name provided above
    Controller.cancelJob("Job3");

    //Cancel all jobs
    Controller.cancelAllJobs();        

    //Cancel a specific jobs
    Controller.cancelJob("JobG");        

    //This is a special function where you can schedule the execution of
    //a code snippet.
    //Arg1: Som job identifier - To use when pause/cancel the job if neccesary.
    //Arg2: start delay (s) - Time until first exec
    //Arg3: period(s) - Time between exec, set to 0 if no repetition is required/wanted.
    //Arg4: repeates - Number of repeates, null if infinit, 0 if no repeat
    //Arg5: code - Any Javascript
    Controller.scheduleExecution("wait05",5,0,0,"print('print once in 5 sec');");

    // *** MISC ***

    //Send an email. Note, needs smtp-server config first
    Controller.SendEmail("torbjorn.carlqvist@davitor.com","anysubject","some body");

    // *** Embedded JSON storage ***

    //Perfect to use when automation settings, states, structures etc must be persisted and
    //the use of an external SQL database is unnecessary

    //Push a string to spcified queue (queue will be created if not exist)
    //This queue is persistant during reboot of the evice
    //All queues and records are stored in the collection "jsonstore.json" which can be found in project folder
    Controller.JSONPush("toca","msg5");
    //Pop a string from the specified queue. FIFO!
    //Returns null if no strings found
    print(Controller.JSONPop("toca"));

    print(Controller.JSONPersist("nisse"));
    print(Controller.JSONPersist("1588058754445","palle"));

    print(Controller.JSONRestore("1588058754445"));

    print(Controller.JSONBrowse("toca"));

    //Change name on multiple local objects
    //This can be used when a group of objects need to have save name prefix.
    //eg. when a sensor has multiple object and you want them to share same sensor name.
    Controller.updateLocalObjectNames("TestBI","newname");

    //This method should be used when javascript forms a link between
    //an external interaface and the object stack.
    //Typically when a button on a HMI should update an binaryInput or
    //a reception of a BLE Beacon should update an analogInput.
    //This method will create an object if no object exists with same profilName property
    Controller.createOrUpdateLocalObject(analogInput,null,"MyObjectName",123);

    //As an addition use this function to control the reliability of the obejct in real time.
    //This will create events and alarms accordingly of intrinsic reporting is enanbled.
    Controller.setLocalObjectReliability(binaryOutput,0,'shorted-loop');
    Controller.setLocalObjectReliability(binaryOutput,0,'no-fault-detected');

    //Yet another addition is this function to set the overridden flag on local objects.
    //The meaning of this flag is to tell BACnet that this physical point is not 
    //longer reliable or commandable.
    Controller.setLocalObjectOverridden(binaryOutput,0,true);
    Controller.setLocalObjectOverridden(binaryOutput,0,false);

    // *** File access ***/

    //Basic file R/W/List for files in project folder (and sub folders)
    Controller.writeFile("file_rw_test.txt","Hello file handling!");
    print(Controller.readFile("file_rw_test.txt"));
    print(Controller.listFiles(".txt",null));
    print(Controller.listFiles(".json","mysubfolder"));    
    print(Controller.removeFiles(".txt",null));    

    // *** OS operations ***/

    //Run commands on host operatice system
    //Result is JSON string
    //The exit_code tells if successful or not.
    //There is a built in timeout if you accedently start a job that does not 
    //stop of it's own. Like doing unlimited ping on Linux
    //No, there is no way to stop a command with Ctrl-C from JS.
    //Note, the process is running in foreground so if DTX dies the process dies too.
    print(Controller.execCommand("ping -n 3 google.com"));

    // *** Serial Ports ***/

    //List all connected serial ports. The response is JSON and can tell a lot
    //about the serial port. For example in which USB socket it is connected.
    print(Controller.listSerialPorts());

    //Connect to a serial port (multiple connection is allowed)
    //Use the serial port name from the response from listSerialPorts() above.
    //As argument form a JSON according to specification.
    //Example of setting up a serial connection to ttyACM0 that handles 
    //delimited responses with a "Return(0d)" at the END with a speed of 115200baud
    //Note that if a connection already occurs in this name it will be closed automatically
    Controller.setupSerialPort("ttyACM0",'{"msgDelim":true,"baudrate":115200,"delimPattern":"0d","delimPosition":"end"}');

    //Send ASCII data to the connected port
    Controller.writeSerialAscii("ttyACM0","hello");

    //Send HEX data to the serial port
    Controller.writeSerialHex("ttyACM0","03"); //Eg. Ctrl-C in HEX

    //Close serial port
    Controller.closeSerialPort("ttyACM0");

    //NOTE: all received serial data enters the event callback "onSerialReceive" 

    /*** MODBUS ***/

    /* Modbus TCP */

    //If needed, use this to simulate a slave on THIS device for test purpose. 
    //Will setup a demo image of regs and coils
    Controller.modbusTCPCreateSlave();

    //Read coils (true/false)
    //Args: Slave IP, Slave Port, Start Addr, Count
    //Return: JSON Array with result
    print(Controller.modbusTCPReadCoils("localhost",502,1,1));

    //Reading input discretes (true/false)
    //Args: Slave IP, Slave Port, Start Addr, Count
    //Return: JSON Array with result
    print(Controller.modbusTCPReadInputDiscretes("localhost",502,1,5));

    //Reading input registers (Analog Inputs)
    //Can be either signed or unsigned 16-bit values.
    //Args: Slave IP, Slave Port, Start Addr, Count
    //Return: JSON Array with result
    print(Controller.modbusTCPReadInputRegisters("localhost",502,1,5));

    //Reading holding registers (Analog Outputs)
    //Can be either signed or unsigned 16-bit values.
    //Args: Slave IP, Slave Port, Start Addr, Count
    //Return: JSON Array with result
    print(Controller.modbusTCPReadHoldingRegisters("localhost",502,1,5));

    //Write to coil (!CAUTION!)
    //Note, always returns null
    //Args: Slave IP, Slave Port, Addr, Status (true=On/1, false=Off/0)
    //Return: JSON Array with result (confirm)
    print(Controller.modbusTCPWriteCoil("localhost",502,1,false));

    //Writing to a holding registers (Analog Outputs)
    //Can be either signed or unsigned 16-bit values.
    //Args: Slave IP, Slave Port, Start Addr, Count
    //Return: JSON Array with result (confirm)
    print(Controller.modbusTCPWriteHoldingRegister("localhost",502,1,123));

    /* Modbus Serial */
    //Note, setting null as port settings defaults to 9600/8N1

    //A mock-up test slave for serial modbus
    //Args: Port, Port Settings, RTU
    Controller.modbusSerialCreateSlave("COM1",null,false);
    //Writing to a MODBUS slave coil via Serial RTU
    //Args: Port, Port Settings,RTU,reg address, value/state 
    Controller.modbusSerialWriteCoil("COM2",null,false,2,true);
    //Reading from a MODBUS slave coil via Serial RTU
    //Args: Port, Port Settings,RTU,reg address
    Controller.modbusSerialReadCoils("COM2",null,false,1,2);

    /*** Controller management ***/

    //Running reInit() will completly clear the JS-engine, stop all jobs and
    //re-actiavate the code and finally call the init() method.
    Controller.reInit();

Events

eventNotificationReceived - Called when an intrinsic report notification is received.

/**
 * Called when controller starts. Good place to do init stuff
 */
function init(){
    print("init!");

}

/***********************************************************************************************
 * Called when the controller receives a notification (intrinsic reporting) from this or another device
 * @param {processIdentifier} Can be used to process events/alarms different consumed. Users choice.
 * @param {Number} initiatingDevice - The device ID that send the event and the device where the triggered object belongs
 * @param {String} object - A convinient string to describe objectype:instance in text
 * @param {Number} objectType - The source object of the event
 * @param {Number} objectInstance - The instance of source object
 * @param {Number} timeStampUTC - EPOC type of timestamp where the event/alarm trigger fired
 * @param {Number} notificationClassInstance - An instance pointer to the connected class
 * @param {Number} priority - Priority of this event
 * @param {Number} eventType - The type of event received. Eg. 'outOfRange'
 * @param {String} messageText - Free text that can be used describing the event
 * @param {String} notifyType - Type of notification. Can be 'alarm' or 'event'
 * @param {Boolean} ackRequired - Tell wether this event/alarm needs acknowledgment or not
 * @param {String} fromState - The previous state
 * @param {String} toState - The current state after the change
 * @param {Object} eventValuesStr - A JSON object of specific values for this particular eventType 
 ***********************************************************************************************/
function eventNotificationReceived(processIdentifier,initiatingDevice,object,objectType,objectInstance,timeStampUTC,notificationClassInstance,priority,eventType,messageText,notifyType,ackRequired,fromState,toState,eventValuesStr){

    var eventDeviceName = Controller.readProperty(initiatingDevice,device,initiatingDevice,objectName);
    var ncObjectName = Controller.readProperty(initiatingDevice,ObjectType.notificationClass,notificationClassInstance,objectName);
    var eventObjectName = Controller.readProperty(initiatingDevice,objectType,objectInstance,objectName);

    var summary = "Received " +eventType+" "+notifyType+" from "+eventDeviceName+" for object "+eventObjectName+" at "+new Date(timeStampUTC).toTimeString();

    print(summary);

    print("Current state:");
    try{
        var eventValues = JSON.parse(eventValuesStr);

        for (var key in eventValues) {
            if (eventValues.hasOwnProperty(key)) {

                print(" - "+key + " " + eventValues[key]);

                /*** Tip, enter code here to act on a specific key/value ***/

            }
        }

    }catch(e){
        print("eventValuesStr not JSON!")
    }

    if (notifyType === 'ackNotification' ){
        var ackedstate = " - Acked state "+toState;
        print(ackedstate);
        summary = summary + ackedstate;
    }else{
        var transistion = " - State changed from "+fromState+" to "+toState;
        print(transistion);
        summary = summary + transistion;
    }

    print(" - Priority "+priority);
    print(" - Ack required "+ackRequired);
    if ( messageText !== undefined && messageText !== "")
        print(" - Message "+messageText);
    print(" - Class "+ncObjectName)

    //Due to the standard, a notifying device does not save un-acked notifs
    //transitions during reboot so it is best practise to make persistant on
    //the receiver side.
    Controller.printToUserLog(summary);

}

/*******************************************************************************
 * Called when Change Of value Notification received from remote device
 * In order to receive Change Of value notification the remote device must first
 * be subscribed on the particular object instance. 
 * Eg. subscribe to Analog Input 0 on device 403
 * - Controller.remoteCOVSubscribe(403,0,0); 
 * It is also possible to subscribe on local objects and in case of a local 
 * notification the device argument is null.
 * Eg. subscribe on local Binary Value 1 and no increment (null)
 * - Controller.COVSubscribe(0,0,null); 
 * @param {Number} epoch - A timestamp (nb of sec since 1970 kind of timestamp.
 * @param {Number} processId - COV process id used by the requester in this subscription
 * @param {Number} deviceId - Remote device that sent the notification
 * @param {Number} objectType - The source object of the event
 * @param {Number} objectInstance - The instance of source object
 * @param {Number} propertyId - The instance of source object* 
 * @param {String} value - The new value.
 *******************************************************************************/
function covNotificationReceived(epoch,processId,deviceId,objectType,objectInstance,propertyId,value){
    print("covNotificationReceived - "+ "Process: " + processId +  " Device: " + deviceId + " ObjectType: " +
                objectType+" ObjectInstance: " + objectInstance + " propertyId: " + propertyId +
                " New Value: " + value + " Time: " + new Date(epoch*1000));

}

/**********************************************************
 * Called when the controller receives a HTTP GET request
 * @param {String} resource - The REST resource
 * @param {Object} params - The HTTP url parameters 
 *********************************************************/
function HTTP_GET(resource,params){
    print(new Date().toISOString() + " HTTP GET - Resource: " + resource + " Params:" + params);
}

/**********************************************************
 * Called when the controller receives a HTTP POST request
 * @param {String} resource - The complete URI in the request
 * @param {Object} payload - The HTTP header parameters
 *********************************************************/
function HTTP_POST(resource,payload){
    print("HTTP POST - Resource:" + resource + " Payload:" + payload);
}

/****************************************************************
 * Called when a Web Socket request is made from a Web Client.
 * 
 * @param {String} context - The key of the page
 * 
 * The context can be made up by three parts URI + APP + SESSION
 * where URI is the only mandatory part.
 *                           
 * @param {String} payloadTxt - The data sent from the WS-client
 *****************************************************************/
function receiveFromWs(context,payloadTxt){
    print("WS Callback from context "+context +" payload:\n" + payloadTxt);

    /** This is a snippet to listen on Web Page Demo Template **/
    /** Use it or remove it                                   **/
    if ( context.indexOf("page-demo") > 0 ) {

        try{
            var jsonObjPayload = JSON.parse(payloadTxt);
            print(jsonObjPayload.msg);
            print(jsonObjPayload.action);

            //Answer to web page using incoming context
            Controller.sendToWS(context,"Oh, Hi! I am a Web Socket request. Look for me in automation.js"); 

        }
        catch(e){
            print("Err"+e);
        }

    }
}

/**
 * Called when a BACnet object on this device changed.
 * @param {type} objectType
 * @param {type} objectInstance
 * @param {type} propertyId
 * @param {type} oldValue
 * @param {type} newValue
 * @returns {undefined}
 */
function localPropertyChanged(objectType,objectInstance,propertyId,oldValue,newValue){
    //print('property changed on '+objectType+':'+objectInstance+' for prop '+propertyId +' where old value is '+oldValue +' and new value is '+newValue);
}

/**************************************************************************************************
 * Callback when this controller receives a I Am notification from a remote device on the network
 * @param {Number} device - Remote device that says hello
 * @param {String} mac - The BACnet mac address of the sending device
 **************************************************************************************************/
function iAmReceived(device,mac){
    //print("IAM received from device:" + device + " MAC:"+mac);
}

/**************************************************************************************************
 * Callback when this controller receives a BACnet private message request
 * @param {Number} sourceDeviceId - Remote device that speeks
 * @param {String} messageClass - See BACnet standard for usage. Could be use as any string data
 * @param {String} messagePriority - See BACnet standard for usage. Could be use as any string data
 * @param {String} message - Send message data
 **************************************************************************************************/
function textMessageReceived(sourceDeviceId,messageClass,messagePriority,message){
    print("textMessageReceived:"+message);
}

/***********************************************************************************************
 * Called when the controller receives a write request from from a remote client
 * on one of the local objects.
 * @param {Number} objectType - The type of the object that is requested to be written
 * @param {Number} objectInstance - The instance of the object that is requested to be written
 * @param {Number} property - The property id that requested to be written
 * @param {Number} priority - The priority of the value that is requested to be written
 * @param {String} value - The new value
 ***********************************************************************************************/
function propertyWritten(objectType,objectInstance,property,priority,value){
    print("propertyWritten - ObjectType: "+objectType+" ObjectInstance: "+ objectInstance + " Property: "+ property + " Priority: "+ priority + " Value: "+value);
}

/***********************************************************************************************
 * Called when a file is put in file input directory
 * The file input directory path is configured in setting page and is by default empty
 * 
 * @param {String} fileName - The name of the file put in the file input directory
 * @param {String} fileContent - The content of the file
 * 
 * Note: There is special mapping for CSV file which are converted to JSON!
 *       Other file types are just received as is
 ***********************************************************************************************/   
function receivedFile(fileName,fileContent){
    print("Received file "+fileName+ " with content "+ fileContent);
}   

/***********************************************************************************************
 * Called receiving data on an activated serial port
 * 
 * Note, see Controller.setupSerialPort(..) for activate a serial port
 * 
 * @param {String} port - Wich port the data comes from
 * @param {String} payload - The data
 * 
 ***********************************************************************************************/   
function onSerialReceive(port, payload){

    print("Serial data received on port: "+port+" data: "+payload); 
}

/**************************************************************************
 * This callback catches unexpected runtime errors like timeouts when 
 * writing or subscribing to a remote device currently absent.
 * @param {String} type - Type or source of exception
 * @param {String} message - Short description of the issue in plain text
 * @param {String} details - Optional information in JSON format
 **************************************************************************/
function exceptionCatch(type,message,details){
    print("Exception catched, type:"+type+" message:" + message + " details:" + details );
}

/**
 * When supported by the platform and enabled in the setting "beacon Station under Bluetooth" 
 * this method is invoked with advertisment from all Bluetooth LE devices nerby
 * 
 * @param {String} mac - The sending Bluetooth device MAC adress
 * @param {String} rssi - The signal strengh of the sending beacon
 * @param {String} bt_payload - The beacon data
 */
function onBTBeaconAdvertising(mac,rssi,bt_payload){

    //Example of whitelist filter
    if( mac.includes("38FE") || mac.includes("CD8E")){

        print("Beacon adv from mac: "+ mac + " rssi: "+rssi+" data:"+bt_payload);

        //Here you can parse the payload from the beacon as you wish
        //If the beacon is a Ruuvi, DTXr has a built in javascript library for the format 5
        //See documentation on DTXr wiki
        //https://collab.davitor.com/redmine/projects/dtxr/wiki
        //More info on Ruuvi format and how to parse here
        //https://ruuvi.com/custom-ruuvi-data-format-in-a-day/

    }
}

/**
 * Received an MQTT publish request
 * @param {type} topic
 * @param {type} payload
 */
function MQTTOnPublish(topic,payload){
    print("MQTT client published " + payload + " on topic " + topic);
}

/**
 * Received a web socket client text response
 * @param {type} text
 */
function WebsocketOnClientReceiveText(text){
    print("WebsocketOnClientReceiveText: "+text);
}

Updated by Torbjorn Carlqvist Admin over 1 year ago · 28 revisions