Javascript » History » Revision 25
Revision 24 (Torbjorn Carlqvist Admin, 01/12/2023 09:33 AM) → Revision 25/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 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>