Project

General

Profile

Javascript » History » Revision 26

Revision 25 (Torbjorn Carlqvist Admin, 01/12/2023 09:35 AM) → Revision 26/28 (Torbjorn Carlqvist Admin, 01/12/2023 09:35 AM)

h1. Javascript guide for DTXr code IDE 

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

 [[#Commands|Commands]] 
 [[#Events|Events]] 


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

 --- 

 h2. Commands 

 <pre><code class="javascript"> 
     //Set this to THIS device id. 
     //The var is used in commands below for convinience. 
     var gThisDeviceId = 123456;    
     //Set this to A REMOTE device id on your network. 
     //The var is used in commands below for convinience. 
     var gRemoteDeviceId = 121212;    
    
     /** LOG/DEBUG **/ 
    
     //Prints to console log window as well as autmation.log 
     //Note that automation.js 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.js 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.js 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)); //Binary Output 
     print(getPropertyTypeById(85)); //presentValue 

     // *** Write a value to an object property *** 
     Controller.writeProperty(gThisDeviceId,analogValue,0,presentValue,priority_1,active); 
     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) 
     Controller.modbusTCPWriteCoil("localhost",502,1,false); 
    
   
     /* 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(); 
 </code></pre> 


 h2. Events 

 h3. *eventNotificationReceived* - Called when an intrinsic report notification is received. 

 <pre><code class="javascript"> 
 /** 
  * 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); 
 } 
 </code></pre>