Obi202 Call Recording

This Node-Red flow monitors syslog messages generated by the Obihai 202 Analog Telephone Adaptor. Upon receiving OFF HOOK, the flow will EXEC a small shell script which uses WGET to download the audio transcript of the call. Upon completion, the file is renamed with a timestamp and the caller ID of the called or calling party in the filename.

See contents of the comment node for specifics. Obi user credentials must be provided in the get cdr html call.

2/4/2017. Updated rename_recordings.sh script to better identify the most recent recording to be renamed.

4/11/2017. Fixed a problem with embedded cr/lf in the renamed filename, which broke SCP transfer to windows hosts for outbound calls. Introduced a 1 second delay to solve a frequent race condition. Ensures CDR records are written to OBI before we read them.

11/18/2018 Updated to work with newer firmware version 3.2.2 (Build: 5898EX). This now uses the callhistory.xml file to access the caller ID.

1/9/2020. Reworked the entire flow so that no shell scripts are necessary. You may choose to record all calls triggered by Syslog OFF HOOK messages as in the past, or you may opt to record manually using a URL. See embedded notes within the flow.

1/17/2020. Added two features to this flow. The first allows you to restart your Obi device periodically. I restart mine daily. It checks to see if there's an active call before blindly restarting, but you probably want to schedule it for the wee hours of the morning despite that.

The second feature allows you to archive your recordings into subdirectories named after the data. For example, Recordings made on January 17, 2020 will be moved to a subdirectory named 2020-01-17.

Please seek the appropriate legal advice to understand the notification requirements for recording calls where you live.

[{"id":"8a328398.edc63","type":"tab","label":"Obi 202 Call Recorder","disabled":false,"info":""},{"id":"e03ad2c0.8f338","type":"file","z":"8a328398.edc63","name":"Write the file","filename":"","appendNewline":false,"createDir":true,"overwriteFile":"true","x":450,"y":420,"wires":[["74fb93d8.06cb4c"]]},{"id":"a8b81569.989bf8","type":"uuid","z":"8a328398.edc63","uuidVersion":"v1","namespaceType":"","namespace":"","namespaceCustom":"","name":"Get a timestamp UUID","field":"filename","fieldType":"msg","x":480,"y":278,"wires":[["20898cd0.f65494"]]},{"id":"20898cd0.f65494","type":"function","z":"8a328398.edc63","name":"Set Path for Temp Recording","func":"// build the filename based on msg.filePath and the UUID value stored in msg.filename with an appropriate file extension.\nmsg.filename = msg.filePath + msg.filename + \".au\";\n\nreturn msg;","outputs":1,"noerr":0,"x":500,"y":318,"wires":[["2e41c34d.b1fbfc"]]},{"id":"2e41c34d.b1fbfc","type":"www-request","z":"8a328398.edc63","name":"Press Obi Call Record Button","method":"GET","ret":"bin","url":"http://192.168.1.47/record.au?port={{{payload}}}","follow-redirects":false,"tls":"","x":500,"y":358,"wires":[["e03ad2c0.8f338"]]},{"id":"d0747dbd.a7f61","type":"function","z":"8a328398.edc63","name":"Set Defaults and Determine Off Hook Port Number","func":"msg.filePath = \"/opt/recordings/\";        // Sets Path to base directory of where recordings will be stored\nflow.set(\"filePath\", msg.filePath);       // for use by archiver\nvar message = msg.payload;\n// regular expression looks for off hook \nvar re = /#(0|1) OFF HOOK/;\n//check for match\nfound =  message.match(re);\n\n//if no match return null\nif (found === null) {\n    return [null, null];\n} else if (found.length === 2 ) {\n    msg.port = found[1];\n    msg.payload = found[1];\n    return [msg, null];\n} else { \n    return [ null , null ];\n}","outputs":2,"noerr":0,"x":570,"y":238,"wires":[["7e7da57e.83ad8c","a8b81569.989bf8"],[]]},{"id":"f344921a.ef62f","type":"www-request","z":"8a328398.edc63","name":"GET Obi202 Call Status","method":"GET","ret":"txt","url":"http://192.168.1.47/callstatus.htm","follow-redirects":true,"tls":"","x":930,"y":300,"wires":[["20474a07.cf20b6"]]},{"id":"20474a07.cf20b6","type":"html","z":"8a328398.edc63","name":"Table Rows","property":"payload","outproperty":"payload","tag":"td","ret":"text","as":"single","x":890,"y":340,"wires":[["e13b9a64.8f1c48"]]},{"id":"e13b9a64.8f1c48","type":"function","z":"8a328398.edc63","name":"Store CID in Context.flow.portXcid","func":"//set regex to find number of active calls\nvar re = /Number of Active Calls: ([0-2])/;\n\nvar found = msg.payload[0].match(re);\n//var found contains array with active \n//call count in [1]\n\nvar port0cid = \"\";\nvar port1cid = \"\";\n\nmsg.found = found; // stash results for later examination\n\nif (found[1] === \"2\") { // get the Connected CID for both ports that will appear in two places\n  if (msg.payload[108] == \"PHONE1\" || msg.payload[109] === \"PHONE1\") port0cid = msg.payload[117];\n  if (msg.payload[108] == \"PHONE2\" || msg.payload[109] === \"PHONE2\") port1cid = msg.payload[117];\n  if (msg.payload[7] == \"PHONE1\" || msg.payload[8] === \"PHONE1\") port0cid = msg.payload[16];\n  if (msg.payload[7] == \"PHONE2\" || msg.payload[8] === \"PHONE2\") port1cid = msg.payload[16];\n  if (msg.port === \"0\") {\n      flow.set(\"port0cid\", port0cid);\n      msg.payload = port0cid;\n  } else {\n      flow.set(\"port1cid\", port1cid);\n      msg.payload = port1cid;\n  }\n} else if (found[1] === \"1\") { //get the Connected CID for a single port\n  if (msg.payload[7] == \"PHONE1\" || msg.payload[8] === \"PHONE1\") port0cid = msg.payload[16];\n  if (msg.payload[7] == \"PHONE2\" || msg.payload[8] === \"PHONE2\") port1cid = msg.payload[16];\n  if (msg.port === \"0\") {\n      flow.set(\"port0cid\", port0cid);\n      msg.payload = port0cid;\n  } else {\n      flow.set(\"port1cid\", port1cid);\n      msg.payload = port1cid;\n  }\n} else {\n  msg.payload = found[1];\n}\nreturn msg;","outputs":1,"noerr":0,"x":960,"y":380,"wires":[[]]},{"id":"7e7da57e.83ad8c","type":"delay","z":"8a328398.edc63","name":"","pauseType":"delay","timeout":"12","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":881,"y":231,"wires":[["f344921a.ef62f"]]},{"id":"74fb93d8.06cb4c","type":"function","z":"8a328398.edc63","name":"Clear Flow CID Variables / build command for next exec node to Rename File and convert to MP3","func":"msg.payload = \"\";\nvar now = new Date();\nnow = now.toLocaleString();\nnow = now.replace(/[ :]/g, \"-\");\nmsg.now = now;\n// Clear the CID flow variable for this call only.\nif (msg.port === \"0\") {\n    var cid = flow.get(\"port0cid\");\n    flow.set(\"port0cid\", \"unknown\");\n}\nif (msg.port === \"1\") {\n    var cid = flow.get(\"port1cid\");\n    flow.set(\"port1cid\", \"unknown\");\n}\n\n//Build Linux Command to post-process the file which includes renaming \n//and possibly conversion to MP3\nvar newName = msg.filePath + \"record_\" + now + \"_\" + cid + \".mp3\";\nmsg.newName = newName;\nmsg.payload = \"ffmpeg -i \" + msg.filename + \" \" + newName + \" && rm \" + msg.filename;\nreturn msg;","outputs":1,"noerr":0,"x":900,"y":420,"wires":[["52120ab1.d66074"]]},{"id":"52120ab1.d66074","type":"exec","z":"8a328398.edc63","command":"","addpay":true,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"Convert to MP3 file and rename with date_CID","x":560,"y":489,"wires":[["c8eb1c07.dce42","ed7dca4d.611158"],["ed7dca4d.611158"],["ed7dca4d.611158"]]},{"id":"331f402.6d39bc","type":"exec","z":"8a328398.edc63","command":"","addpay":true,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"Remove small files left by Panasonic phone that insists on going off hook briefly after each call.","x":710,"y":575,"wires":[[],[],[]]},{"id":"c8eb1c07.dce42","type":"function","z":"8a328398.edc63","name":"build msg.payload for next exec node....","func":"msg.payload = \"\";\nmsg.payload = 'find ' + msg.filePath + ' -name \"*.mp3\" -size -10k -delete';\nreturn msg;","outputs":1,"noerr":0,"x":540,"y":532,"wires":[["331f402.6d39bc"]]},{"id":"ed7dca4d.611158","type":"debug","z":"8a328398.edc63","name":"Connect stuff here to see messages","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":1108,"y":500,"wires":[]},{"id":"9cce7373.14a12","type":"udp in","z":"8a328398.edc63","name":"Obi Syslog on UDP 1514","iface":"","port":"1514","ipv":"udp4","multicast":"false","group":"","datatype":"utf8","x":128,"y":580,"wires":[["a9cd4fb1.53cf9"]]},{"id":"a9cd4fb1.53cf9","type":"link out","z":"8a328398.edc63","name":"Share Syslog with other Flows","links":["6523fe9f.fd5f8"],"x":73,"y":340,"wires":[]},{"id":"13832c4d.ec9114","type":"http response","z":"8a328398.edc63","name":"HTTP Response","statusCode":"200","headers":{},"x":508,"y":80,"wires":[]},{"id":"fb746882.793848","type":"http in","z":"8a328398.edc63","name":"Call Record HTTP Request","url":"callrecord","method":"get","upload":false,"swaggerDoc":"","x":128,"y":80,"wires":[["13832c4d.ec9114","5f99436f.7d4e7c"]]},{"id":"5f99436f.7d4e7c","type":"function","z":"8a328398.edc63","name":"Simulate Syslog Off Hook Message when user sends proper urlencoded method.","func":"if ( msg.payload.port0 === \"record\" ) {\n    msg.payload = \"#0 OFF HOOK\";\n} else if ( msg.payload.port1 === \"record\" ) {\n    msg.payload = \"#1 OFF HOOK\";\n} else {\n    msg.payload = null;\n}\nreturn msg;","outputs":1,"noerr":0,"x":338,"y":140,"wires":[[]]},{"id":"36ef9724.ceedb8","type":"comment","z":"8a328398.edc63","name":"<=== Connect to \"Set Defaults...\" to enable on demand recording ( disconnect Syslog )","info":"Connect function node \"Simulate Syslog Off\nHook Message\" to the \"Set Defaults...\"\nfunction node in order to enable recording of calls manually\nusing a URI.\n\nURI = \nhttp://Node-Red-IPADDR:1880/callrecord?portx=record\n\nx can be 0 or 1, corresponding to the port your telephone\nis connected.\n\nThere is no authentication.  This is meant for use in a\nhome environment behind a firewall.\n","x":958,"y":139,"wires":[]},{"id":"41e6f051.de3a6","type":"comment","z":"8a328398.edc63","name":"Connect Syslog to \"Set Defaults...\" to enable recording of all calls ( disconnect HTTP Request Path )","info":"","x":358,"y":660,"wires":[]},{"id":"3ec68494.60ea5c","type":"comment","z":"8a328398.edc63","name":"Semi-Detailed Instructions","info":"This flow replaces an older version that relied on\ntwo linux shell scripts to press the record button\nusing a web link and to rename/convert the resulting\naudio file to mp3.\n\nThis flow accomplishes everything with out the aid\nof shell scripts.  It does have some pre-requisite\nsteps that you must follow in order to make it work.\n\n1. You have to decide whether or not you are going\n   to record all calls, or if you want to start\n   recording manually using a web service.\n   To trigger recording manually, you can place a \n   browser shortcut on your desktop with the URL\n   for port 0 or port 1.  Or you can incorporate\n   that uri into a web page or home automation of\n   your choice. \n\nPort 0: http://node-red-IP:1880/callrecord?port0=record\nPort 1: http://node-red-IP:1880/callrecord?port1=record\n\nAssumes node red listens on the standard port. \nThere is no authentication as this is meant to \nfunction in a home LAN environment.  Feel free to\nadd whatever suits your security posture.\n\n2. If you decide you want to record all calls, then \n   disconnect the wire from tje 1st amd second function\n   nodes.  Connect the wire from the Obi Syslog UDP\n   in node to the 2nd function node.  Once this is\n   done, off hook messages from Syslog will trigger\n   recording for the appropriate port.\n\n3. For that to work, you have to set up your \n   OBI syslog to use a udp port above 1024.  \n   I chose 1514 UDP.  Please refer to OBI \n   documentation.  My obi is managed by the \n   obitalk.com portal.  The Syslog value can be\n   changed using the Advanced Admin -> System \n   Management -> Device Admin page.  You simply\n   change the syslog port from 514 to 1514 or\n   whatever you choose to use in the UDP In node.\n\n4. Make a subdirectory on your node-red host, or \n   network shared drive that is write accessible\n   to the user account under which node-red is \n   running.  Edit the second function node\n   named \"Set Defaults and...\" to include this \n   full path from root in the msg.filePath \n   entry.\n\n5. I wrote this to convert .au files to mp3.  \n   Either install ffmpeg with mp3 support or you\n   can replace the ffmpeg command in the \n   \"Clear Flow CID Variables / build command...\"\n   function node with one that renames the file\n   using the provided CID and localestring values.\n\n6. There are comment reminders next to nodes that\n   require changes.  There are two http nodes \n   that require you add basic authentication\n   values.  I've also marked where to set the\n   recording path, and left reminders of where\n   to connect Syslog for automatic recording, or\n   where to connect the HTTP In node for manually\n   triggered recording.\n\nWhat you'll end up with are reasonably named files\nthat show you what date and time the call ends along \nwith the CID of the remote party, in a single\nsubdirectory.\n","x":128,"y":40,"wires":[]},{"id":"5250045b.8e4dfc","type":"comment","z":"8a328398.edc63","name":"<== Basic Auth","info":"Don't forget to provide the admin user account and password\nfor your OBI here.","x":718,"y":360,"wires":[]},{"id":"ebf44c5.45255b","type":"comment","z":"8a328398.edc63","name":"<== Basic Auth","info":"Don't forget to provide the admin user account and password\nfor your OBI here.","x":1138,"y":300,"wires":[]},{"id":"9a10018e.64cec","type":"comment","z":"8a328398.edc63","name":"Set Recording Path ==>","info":"Set msg.filePath to a directory in which you want to store\nyour recordings.  The directory must be writeable by the\nuser account used by node-red.\n\nPath must begin and end with /\n\nexample \"/opt/recordings/\"\n\n","x":140,"y":240,"wires":[]},{"id":"9f281c17.6efe3","type":"comment","z":"8a328398.edc63","name":"Share Syslog with other flows-Optional","info":"","x":168,"y":300,"wires":[]},{"id":"560568a7.24e998","type":"comment","z":"8a328398.edc63","name":"Delay 12S - Not my best moment","info":"Delay 12s is here because the Call Status page,\nwhich holds the phone number of the remote party\nisn't populated until the remote party is connected.\n\nSo, if you read the page immediately, you end up \nwith one line saying there are 0 active calls.\nThanks Obihai!\n\nSince off hook messages do not provide any\ncall direction information, it's easier to wait a\nreasonable amount of time for the information to \nappear for outbound calls.\n\nYou may find that 12 seconds is not enough.  For \nme it's just right.\n\nYou may also occasionally hang up after answering\na call in less than these 12 seconds and end up\nwith a recording whose filename lacks the CID \ninformation showing \"unknown\" instead.  Oh well!\n\n","x":1128,"y":220,"wires":[]},{"id":"41b29511.fd57ac","type":"comment","z":"8a328398.edc63","name":"Last 2 nodes unique to my Panasonic Phone","info":"My panasonic Dect phone insists on going off hook\nbriefly after each call is concluded.  It annoyed\nme to have short recordings of dialtone left over.\nI noticed they were all under 10K in size.  Any\nrecording that short I won't care about anyway so \nI just delete them.\n\nThis won't be a problem with manually triggered\nrecording and may never affect you unless you're\n\"lucky\" enough to own this phone.  It's a great \ndevice in all other aspects.","x":988,"y":620,"wires":[]},{"id":"b084494e.b6fa18","type":"comment","z":"8a328398.edc63","name":"Check Call Recording Laws!!","info":"Please bear in mind that it is your responsibility\nto understand the laws governing call recording.\n\nYour jurisdiction may require 2 party consent.  It\nmay not. I can't advise you.  I'm not a lawyer.  \n\nBut I am smart enough to look it up for myself.\n\nAs published, you must connect the wires from the \nUDP in node to the 2nd function node, or from the\nHTTP In node to the 2nd function node to make this\nflow work. \n\nI am intentionally delivering it in a disabled \nstate.  It requires you to choose to record.\n","x":137,"y":620,"wires":[]},{"id":"1c5ceddb.b83572","type":"comment","z":"8a328398.edc63","name":"Check Call Recording Laws!!","info":"Please bear in mind that it is your responsibility\nto understand the laws governing call recording.\n\nYour jurisdiction may require 2 party consent.  It\nmay not. I can't advise you.  I'm not a lawyer.  \n\nBut I am smart enough to look it up for myself.\n\nAs published, you must connect the wires from the \nUDP in node to the 2nd function node, or from the\nHTTP In node to the 2nd function node to make this\nflow work. \n\nI am intentionally delivering it in a disabled \nstate.  It requires you to choose to record.\n","x":777,"y":100,"wires":[]},{"id":"f50b26b3.b29d68","type":"inject","z":"8a328398.edc63","name":"Set Schedule","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"57 03 * * *","once":false,"onceDelay":"","x":111,"y":876,"wires":[["733c614.84088a"]]},{"id":"3f19b9ad.5cf2e6","type":"debug","z":"8a328398.edc63","name":"Debug Output","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":539,"y":1104,"wires":[]},{"id":"63879953.c73ef8","type":"html","z":"8a328398.edc63","name":"Check TD Element for Calls In Progress","property":"","tag":"td","ret":"text","as":"multi","x":206,"y":992,"wires":[["1b67b290.d8108d"]]},{"id":"1b67b290.d8108d","type":"switch","z":"8a328398.edc63","name":"Check for Active Call: No/Yes","property":"payload","propertyType":"msg","rules":[{"t":"cont","v":"Number of Active Calls: 0","vt":"str"},{"t":"regex","v":"Number of Active Calls: 1|Number of Active Calls: [12]","vt":"str","case":false},{"t":"else"}],"checkall":"true","repair":false,"outputs":3,"x":166,"y":1049,"wires":[["3d792074.fea2"],["72ee9414.aa18dc"],[]],"outputLabels":["No Active Call","There's a Call in Progress",""]},{"id":"72ee9414.aa18dc","type":"change","z":"8a328398.edc63","name":"Yes a Call is in Progress: - Set Failure Message","rules":[{"t":"set","p":"payload","pt":"msg","to":"Failed to reboot - Call in Progress","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":226,"y":1104,"wires":[["3f19b9ad.5cf2e6"]]},{"id":"be632fb0.1618a","type":"comment","z":"8a328398.edc63","name":"Obi Reboot: Add IP-Address and Basic Auth to two www-request nodes.  Set Schedule to a time when people aren't using the phone.","info":"","x":469,"y":845,"wires":[]},{"id":"a1cbf2da.ea922","type":"comment","z":"8a328398.edc63","name":"Before blindly rebooting your Obi...","info":"","x":186,"y":961,"wires":[]},{"id":"2ccf9c67.a1ce34","type":"change","z":"8a328398.edc63","name":"Remove msg.headers","rules":[{"t":"delete","p":"headers","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":339,"y":921,"wires":[["63879953.c73ef8"]]},{"id":"ea2b708.3e9ba9","type":"comment","z":"8a328398.edc63","name":"<<--- Here","info":"","x":615,"y":875,"wires":[]},{"id":"54e845.734f37bc","type":"comment","z":"8a328398.edc63","name":"<<--- and here","info":"","x":724,"y":1033,"wires":[]},{"id":"733c614.84088a","type":"www-request","z":"8a328398.edc63","name":"Check Obi202 Calls Status Page","method":"GET","ret":"txt","url":"http://192.168.1.47/callstatus.htm","follow-redirects":false,"tls":"","x":379,"y":876,"wires":[["2ccf9c67.a1ce34"]]},{"id":"3d792074.fea2","type":"www-request","z":"8a328398.edc63","name":"Reboot OBI202","method":"GET","ret":"txt","url":"http://192.168.1.47/rebootgetconfig.htm?reboot","follow-redirects":true,"tls":"","x":538,"y":1036,"wires":[["3f19b9ad.5cf2e6"]]},{"id":"f4535f9c.3ef64","type":"inject","z":"8a328398.edc63","name":"At 3:05 am, do stuff","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"05 03 * * *","once":false,"onceDelay":0.1,"x":146,"y":1280,"wires":[["f4edb8d0.c12da8","4aef090.a8517f8"]]},{"id":"323a09f6.edeb56","type":"exec","z":"8a328398.edc63","command":"","addpay":true,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"Make a subdirectory for yesterday's recordings","x":767,"y":1279,"wires":[["8328968b.001f18"],["8328968b.001f18"],["8328968b.001f18"]]},{"id":"8328968b.001f18","type":"debug","z":"8a328398.edc63","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":1101,"y":1279,"wires":[]},{"id":"25331129.1e7ade","type":"exec","z":"8a328398.edc63","command":"","addpay":true,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"Use the command to move yesterday's recordings to their own subdirectory","x":680,"y":1420,"wires":[["8328968b.001f18"],["8328968b.001f18"],["8328968b.001f18"]]},{"id":"b985ea3f.82f748","type":"function","z":"8a328398.edc63","name":"Build Command to Move Yesterday's Files","func":"var now = new Date(new Date().setDate(new Date().getDate()-1))\nnow = now.toLocaleDateString();\nmsg.payload = \"cd /opt/recordings && mv record_\" + now + \"*.mp3 $(date -d \\\"yesterday 13:00\\\" +%F)\";\nreturn msg;","outputs":1,"noerr":0,"x":210,"y":1420,"wires":[["25331129.1e7ade"]]},{"id":"f4edb8d0.c12da8","type":"delay","z":"8a328398.edc63","name":"Wait 5 Seconds","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":130,"y":1345,"wires":[["b985ea3f.82f748"]]},{"id":"b795e9f6.43a538","type":"comment","z":"8a328398.edc63","name":"Daily archiver for Obi Recordings","info":"","x":160,"y":1240,"wires":[]},{"id":"4aef090.a8517f8","type":"function","z":"8a328398.edc63","name":"Build Mkdir Command","func":"filePath = flow.get(\"filePath\");\nmsg.payload = \"mkdir \" + filePath + \"$(date +%F)\";\nreturn msg;\n\n//cd /opt/recordings && mkdir $(date +%F)","outputs":1,"noerr":0,"x":432,"y":1280,"wires":[["323a09f6.edeb56"]]},{"id":"d6f731f1.5f90e","type":"comment","z":"8a328398.edc63","name":"Connect either Syslog output or HTTP In here","info":"","x":550,"y":200,"wires":[]}]

Flow Info

Created 8 years ago
Updated 4 years, 7 months ago
Rating: not yet rated

Owner

Actions

Rate:

Node Types

Core
  • change (x2)
  • comment (x17)
  • debug (x3)
  • delay (x2)
  • exec (x4)
  • file (x1)
  • function (x8)
  • html (x2)
  • http in (x1)
  • http response (x1)
  • inject (x2)
  • link out (x1)
  • switch (x1)
  • udp in (x1)
Other
  • tab (x1)
  • uuid (x1)
  • www-request (x4)

Tags

  • VoIP
  • telephony
  • obi
  • obihai
  • obi202
  • recording
  • call
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option