Elk M1 Gold Alarm System TCP Interface
Author: Mike Thompson, GitHub: mike-linus Date: 14/8/2016 Updated: 22/2/2017 - added time delay for multiple word speech
The Elk M1 alarm is a very capable alarm system that provides TCP network access via the optional ethernet interface.
Elk fortunately documented the command set in their RS-232 Interface Specification ref. ELK-M1_RS232_PROTOCOL
Some excellent Interfaces have been built using node.js ref. https://github.com/kevinohara80/elkington and Perl ref. https://sourceforge.net/projects/elkm1control/ but are missing key components and are not designed to work with node-RED or MQTT.
The example flow shows how to monitor alarm messages, issue speech commands and other general commands. A continuous speech routine is also included.
To use, alter the TCP nodes to point to the IP address of your alarm.
Note: The nodes have been configured for use on the internal network using the default non-secure port 2101.
[{"id":"a4145a1e.5beba8","type":"switch","z":"cddb8bb1.322478","name":"Switch Zone Message","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"1","vt":"str"},{"t":"eq","v":"2","vt":"str"},{"t":"eq","v":"3","vt":"str"},{"t":"eq","v":"4","vt":"str"},{"t":"eq","v":"5","vt":"str"},{"t":"eq","v":"6","vt":"str"},{"t":"eq","v":"7","vt":"str"},{"t":"eq","v":"8","vt":"str"},{"t":"eq","v":"9","vt":"str"},{"t":"eq","v":"10","vt":"str"},{"t":"eq","v":"11","vt":"str"},{"t":"eq","v":"12","vt":"str"},{"t":"eq","v":"13","vt":"str"}],"checkall":"false","outputs":13,"x":325,"y":343.5,"wires":[["8c7e6025.7381a"],["ee84c7b2.117b38"],["8cfa65ab.730598"],["7265572.f8d9aa8"],["dd3c380.f22c3c8"],["db0e8694.24f178"],["bb08d547.44f728"],["29b412e1.d64bee"],["6ac803d4.9537fc"],["549b6576.ab649c"],["79fa94cc.86056c"],["ce4e566d.31b1a8"],["924523b2.6dbae"]]},{"id":"9e239776.61dc68","type":"function","z":"cddb8bb1.322478","name":"Extract data to JSON String","func":"// Extracts alarm info and converts to freeboard json format\n\nvar boostate;\nvar alarminfo = msg.payload.split(' ');\n\nif (alarminfo[1]==\"violated\" || alarminfo[1]==\"on\")\n{\n boostate=true;\n} \nelse\n{\n boostate=false;\n}\n\nmsg.topic = alarminfo[0];\nmsg.payload = boostate;\n\nreturn msg;","outputs":"1","noerr":0,"x":170,"y":188,"wires":[["a4145a1e.5beba8"]]},{"id":"18c8562b.e737aa","type":"function","z":"cddb8bb1.322478","name":"Extract Alarm Code","func":"var strMsg = msg.payload.toString();\nvar strCode = strMsg.substring(2,4);\n\nmsg.topic = strCode;\nmsg.payload = strMsg;\n\nreturn msg;","outputs":"1","noerr":0,"x":350,"y":94,"wires":[["f452b495.0bad48"]]},{"id":"8d3271c0.72cd9","type":"tcp in","z":"cddb8bb1.322478","name":"TCP input from ElkM1","server":"client","host":"192.168.2.28","port":"2101","datamode":"stream","datatype":"buffer","newline":"","topic":"","base64":false,"x":134,"y":119,"wires":[["18c8562b.e737aa"]]},{"id":"78b6a247.87495c","type":"mqtt out","z":"cddb8bb1.322478","name":"","topic":"ElkM1/zone","qos":"","retain":"true","broker":"fca9574c.0356a8","x":960,"y":57,"wires":[]},{"id":"f9df92d2.06207","type":"mqtt out","z":"cddb8bb1.322478","name":"","topic":"ElkM1/arm","qos":"","retain":"true","broker":"fca9574c.0356a8","x":957,"y":151,"wires":[]},{"id":"f39023b7.0c6fe","type":"function","z":"cddb8bb1.322478","name":"Extract Zone Change Message","func":"\nvar strZone = msg.payload.substring(4,7);\nvar intZone = parseInt(strZone);\nvar strIntZone = intZone.toString();\nvar strState = msg.payload.substring(7,10);\nvar strStateText = '';\n\nif (strState == '900')\n{\n strStateText = 'violated';\n} \nelse\n{\n if (strState == '200')\n {\n\t strStateText = 'normal';\n } \n else\n {\n\t if (strState == '100')\n\t {\n\t\t strStateText = 'off'; \n\t } \n\t else\n\t {\n\t if (strState == 'B00')\n\t {\n\t\t strStateText = 'on'; \n }\n\t }\n }\n}\nmsg.payload = strIntZone + ' ' + strStateText;\n\nreturn msg;","outputs":"1","noerr":0,"x":719,"y":82,"wires":[["78b6a247.87495c","9e239776.61dc68"]]},{"id":"f452b495.0bad48","type":"switch","z":"cddb8bb1.322478","name":"","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"ZC","vt":"str"},{"t":"eq","v":"AS","vt":"str"}],"checkall":"true","outputs":2,"x":508,"y":118,"wires":[["f39023b7.0c6fe"],["78a29020.875d7"]]},{"id":"78a29020.875d7","type":"function","z":"cddb8bb1.322478","name":"Extract Alarm State Message","func":"var strArm = msg.payload.substring(4,5);\nvar strArmText = '';\n\nif (strArm == '0')\n{\n strArmText = 'Disarmed';\n} \nelse\n{\n strArmText = 'Armed';\n}\n\nmsg.payload = strArmText;\n\nreturn msg;","outputs":"1","noerr":0,"x":713,"y":151,"wires":[["f9df92d2.06207"]]},{"id":"f0dc30eb.0f23d","type":"inject","z":"cddb8bb1.322478","name":"Alarm Status Request","topic":"","payload":"as","payloadType":"str","repeat":"","crontab":"","once":false,"x":144.99998474121094,"y":768.9999847412109,"wires":[["957b9391.6a847"]]},{"id":"957b9391.6a847","type":"function","z":"cddb8bb1.322478","name":"Format command","func":"\nvar writeAscii = function(command) {\n var ascii;\n var len;\n var lenString;\n var cc;\n \n // add the reserved 00 from elk protocol\n command = command + '00';\n \n // calculate the ascii length and prepend the string\n len = command.length + 2;\n lenString = len.toString(16).toUpperCase();\n if(lenString.length == 1) lenString = '0' + lenString;\n ascii = lenString + command;\n \n // get the checksum and append\n cc = calcChecksum(ascii);\n ascii = ascii + cc + '\\r\\n';\n\n return ascii;\n}\n\nvar calcChecksum = function(string) {\n var buf = new Buffer(string);\n\n // Calculate the modulo 256 checksum\n var sum = 0;\n for (var i = 0, l = buf.length; i < l; i++) {\n sum = (sum + buf[i]) % 256;\n }\n // Find the compliment\n sum = 256 - sum;\n\n // Convert to a two byte uppercase hex value\n var chars = sum.toString(16).toUpperCase();\n if (chars.length == 1) chars = '0' + chars;\n return chars;\n}\n\nmsg.payload = writeAscii(msg.payload);\n\nreturn msg;","outputs":"1","noerr":0,"x":343.9999694824219,"y":709.9999847412109,"wires":[["968ac616.697538"]]},{"id":"968ac616.697538","type":"tcp request","z":"cddb8bb1.322478","server":"192.168.2.28","port":"2101","out":"char","splitc":"\\n","name":"TCP request to ElkM1","x":442.99998474121094,"y":768.9999847412109,"wires":[["166da14b.e9925f"]]},{"id":"166da14b.e9925f","type":"function","z":"cddb8bb1.322478","name":"Extract Alarm Code","func":"var strMsg = msg.payload.toString();\nvar strCode = strMsg.substring(2,4);\n\nmsg.topic = strCode;\nmsg.payload = strMsg;\n\nreturn msg;","outputs":"1","noerr":0,"x":359.99998474121094,"y":834.9999847412109,"wires":[["bd35d5d8.42ca28"]]},{"id":"bd35d5d8.42ca28","type":"debug","z":"cddb8bb1.322478","name":"","active":true,"console":"false","complete":"false","x":573.9999847412109,"y":837.9999847412109,"wires":[]},{"id":"b3f796f8.4c0868","type":"function","z":"cddb8bb1.322478","name":"Send Commands","func":"var writeAscii = function(command) {\n var ascii;\n var len;\n var lenString;\n var cc;\n \n // add the reserved 00 from elk protocol\n command = command + '00';\n \n // calculate the ascii length and prepend the string\n len = command.length + 2;\n lenString = len.toString(16).toUpperCase();\n if(lenString.length == 1) lenString = '0' + lenString;\n ascii = lenString + command;\n \n // get the checksum and append\n cc = calcChecksum(ascii);\n ascii = ascii + cc + '\\r\\n';\n\n return ascii;\n}\n\nvar calcChecksum = function(string) {\n var buf = new Buffer(string);\n\n // Calculate the modulo 256 checksum\n var sum = 0;\n for (var i = 0, l = buf.length; i < l; i++) {\n sum = (sum + buf[i]) % 256;\n }\n // Find the compliment\n sum = 256 - sum;\n\n // Convert to a two byte uppercase hex value\n var chars = sum.toString(16).toUpperCase();\n if (chars.length == 1) chars = '0' + chars;\n return chars;\n}\n\nmsg.payload = writeAscii('sw' + msg.payload);\n\nreturn msg;","outputs":1,"noerr":0,"x":351.9999694824219,"y":611.9999694824219,"wires":[["8cd545cb.732ab8"]]},{"id":"667098a9.998f68","type":"splitter","z":"cddb8bb1.322478","name":"","property":"payload","x":154.99998474121094,"y":569.9999694824219,"wires":[["a1fc7a9f.d1e978"]]},{"id":"8cd545cb.732ab8","type":"tcp out","z":"cddb8bb1.322478","host":"192.168.2.28","port":"2101","beserver":"client","base64":false,"end":true,"name":"TCP output to ElkM1","x":543.9999694824219,"y":557.9999694824219,"wires":[]},{"id":"d4e70092.2b19","type":"function","z":"cddb8bb1.322478","name":"Convert String to Word Codes","func":"var words = {\n custom1 : 1,\n custom2 : 2,\n custom3 : 3,\n custom4 : 4,\n custom5 : 5,\n custom6 : 6,\n custom7 : 7,\n custom8 : 8,\n custom9 : 9,\n custom10 : 10,\n zero : 21,\n one : 22,\n two : 23,\n three : 24,\n four : 25,\n five : 26,\n six : 27,\n seven : 28,\n eight : 29,\n nine : 30,\n ten : 31,\n eleven : 32,\n twelve : 33,\n thirteen : 34,\n fourteen : 35,\n fifteen : 36,\n sixteen : 37,\n seventeen : 38,\n eighteen : 39,\n nineteen : 40,\n twenty : 41,\n thirty : 42,\n forty : 43, // note: this is actually misspelled in the protocol docs as 'fourty'\n fifty : 44,\n sixty : 45,\n seventy : 46,\n eighty : 47,\n ninety : 48,\n hundred : 49,\n thousand : 50,\n silence200ms : 51,\n silence500ms : 52,\n tone800hz : 53,\n a : 54,\n access : 55,\n acknowledged : 56,\n ac_power : 57,\n activate : 58,\n activated : 59,\n active : 60,\n adjust : 61,\n air : 62,\n alarm : 63,\n alert : 64,\n all : 65,\n am : 66,\n an : 67,\n and : 68,\n answer : 69,\n any : 70,\n are : 71,\n area : 72,\n arm : 73,\n armed : 74,\n at : 75,\n attic : 76,\n audio : 77,\n auto : 78,\n authorized : 79,\n automatic : 80,\n automation : 81,\n auxiliary : 82,\n away : 83,\n b : 84,\n back : 85,\n barn : 86,\n basement : 87,\n bathroom : 88,\n battery : 89,\n bedroom : 90,\n been : 91,\n bell : 92,\n bottom : 93,\n break : 94,\n breakfast : 95,\n bright : 96,\n building : 97,\n burglar : 98,\n button : 99,\n by : 100,\n bypassed : 101,\n cabinet : 102,\n call : 103,\n camera : 104,\n cancel : 105,\n carbon_monoxide : 106,\n card : 107,\n center : 108,\n central : 109,\n change : 110,\n check : 111,\n chime : 112,\n circuit : 113,\n clear : 114,\n closed : 115,\n closet : 116,\n code : 117,\n cold : 118,\n condition : 119,\n connect : 120,\n control : 121,\n cool : 122,\n cooling : 123,\n corner : 124,\n crawlspace : 125,\n danger : 126,\n day : 127,\n deck : 128,\n decrease : 129,\n defective : 130,\n degrees : 131,\n delay : 132,\n den : 133,\n denied : 134,\n detected : 135,\n detector : 136,\n device : 137,\n dial : 138,\n dialing : 139,\n dim : 140,\n dining_room : 141,\n disable : 142,\n disarm : 143,\n disarmed : 144,\n dock : 145,\n door : 146,\n doors : 147,\n down : 148,\n driveway : 149,\n east : 150,\n emergency : 151,\n enable : 152,\n end : 153,\n energy : 154,\n enrollment : 155,\n enter : 156,\n entering : 157,\n entertainment : 158,\n enter_the : 159,\n entry : 160,\n environment : 161,\n equipment : 162,\n error : 163,\n evacuate : 164,\n event : 165,\n exercise : 166,\n expander : 167,\n exit : 168,\n exterior : 169,\n f : 170,\n fail : 171,\n failure : 172,\n family_room : 173,\n fan : 174,\n feed : 175,\n fence : 176,\n fire : 177,\n first : 178,\n flood : 179,\n floor : 180,\n followed : 181,\n force : 182,\n fountain : 183,\n foyer : 184,\n freeze : 185,\n front : 186,\n full : 187,\n furnace : 188,\n fuse : 189,\n game : 190,\n garage : 191,\n gas : 192,\n gate : 193,\n glass : 194,\n go : 195,\n good : 196,\n goodbye : 197,\n great : 198,\n group : 199,\n guest : 200,\n gun : 201,\n hall : 202,\n hallway : 203,\n hanging_up : 204,\n hang_up : 205,\n has : 206,\n has_expired : 207,\n have : 208,\n hear_menu_options : 209,\n heat : 210,\n help : 211,\n high : 212,\n hold : 213,\n home : 214,\n hot : 215,\n hottub : 216,\n house : 217,\n humidity : 218,\n hvac : 219,\n if : 220,\n immediately : 221,\n in : 222,\n inches : 223,\n increase : 224,\n inner : 225,\n input : 226,\n inside : 227,\n instant : 228,\n interior : 229,\n in_the : 230,\n intruder : 231,\n intruder_message: 473,\n intrusion : 232,\n invalid : 233,\n is : 234,\n is_about_to_expire : 235,\n is_active : 236,\n is_armed : 237,\n is_canceled : 238,\n is_closed : 239,\n is_disarmed : 240,\n is_low : 241,\n is_off : 242,\n is_ok : 243,\n is_on : 244,\n is_open : 245,\n jacuzzi : 246,\n jewelry : 247,\n keep : 248,\n key : 249,\n keypad : 250,\n kitchen : 251,\n lamp : 252,\n laundry : 253,\n lawn : 254,\n leak : 255,\n leave: 256,\n left : 257,\n less : 258,\n level : 259,\n library : 260,\n light : 261,\n lights : 262,\n line : 263, \n living_room : 264,\n loading : 265,\n lobby : 266,\n location : 267,\n lock : 268,\n low : 269,\n lower : 270,\n m : 271,\n machine : 272,\n mail : 273,\n main : 274,\n mains : 275,\n manual : 276,\n master : 277,\n max : 278,\n media : 279,\n medical : 280,\n medicine : 281,\n memory : 282,\n menu : 283,\n message : 284,\n middle : 285,\n minute : 286,\n missing : 287,\n mode : 288,\n module : 289,\n monitor : 290,\n more : 291,\n motion : 292,\n motor : 293,\n next : 294,\n night : 295,\n no : 296,\n normal : 297,\n north : 298,\n not : 299,\n notified : 300,\n now : 301,\n number : 302,\n nursery : 303,\n of : 304,\n off : 305,\n office : 306,\n oh : 307,\n ok : 308,\n on : 309,\n online : 310,\n only : 311,\n open : 312,\n operating : 313,\n option : 314,\n or : 315,\n other : 316,\n out : 317,\n outlet : 318,\n output : 319,\n outside : 320,\n over : 321,\n overhead : 322,\n panel : 323,\n panic : 324,\n parking : 325,\n partition : 326,\n patio : 327,\n pause : 328,\n perimeter : 329,\n personal : 330,\n phone : 331,\n place : 332,\n play : 333,\n please : 334,\n plus : 335,\n pm : 336,\n police : 337,\n pool : 338,\n porch : 339,\n port : 340,\n pound : 341,\n pounds : 342,\n power : 343,\n press : 344,\n pressure : 345,\n problem : 346,\n program : 347,\n protected : 348,\n pump : 349,\n radio : 350,\n raise : 351,\n ready : 352,\n rear : 353,\n receiver : 354,\n record : 355,\n recreation : 356,\n relay : 357,\n remain_calm : 358,\n remote : 359,\n repeat : 360,\n report : 361,\n reporting : 362,\n reset : 363,\n restored : 364,\n return : 365,\n right : 366,\n roof : 367,\n room : 368,\n running : 369,\n safe : 370,\n save : 371,\n screen : 372,\n second : 373,\n secure : 374,\n security : 375,\n select : 376,\n sensor : 377,\n serial : 378,\n service : 379,\n set : 380,\n setback : 381,\n setpoint : 382,\n setting : 383,\n shed : 384,\n shipping : 385,\n shock : 386,\n shop : 387,\n shorted : 388,\n shunted : 389,\n side : 390,\n silence : 391,\n siren : 392,\n sliding : 393,\n smoke : 394,\n someone : 395,\n south : 396,\n spare : 397,\n speaker : 398,\n sprinkler : 399,\n stairs : 400,\n stairway : 401,\n star : 402,\n start : 403,\n status : 404,\n stay : 405,\n stock : 406,\n stop : 407,\n storage : 408,\n storm : 409,\n studio : 410,\n study : 411,\n sump : 412,\n sun : 413,\n switch : 414,\n system : 415,\n tamper : 416,\n tank : 417,\n task : 418,\n telephone : 419,\n television : 420,\n temperature : 421,\n test : 422,\n thank_you : 423,\n that : 424,\n the : 425,\n theater : 426,\n thermostat : 427,\n third : 428,\n time : 429,\n toggle : 430,\n top : 431,\n transformer : 432,\n transmitter : 433,\n trespassing : 434,\n trouble : 435,\n turn : 436,\n twice : 437,\n type : 438,\n under : 439,\n unit : 440,\n unlocked : 441,\n unoccupied : 442,\n up : 443,\n user : 444,\n utility : 445,\n vacation : 446,\n valve : 447,\n video : 448,\n violated : 449,\n visitor : 450,\n wake_up : 451,\n walk : 452,\n wall : 453,\n warehouse : 454,\n warning : 455,\n water : 456,\n way : 457,\n welcome : 458,\n west : 459,\n what : 460,\n when : 461,\n where : 462,\n will : 463,\n window : 464,\n windows : 465,\n with : 466,\n work : 467,\n yard : 468,\n year : 469,\n you : 470,\n zone : 471,\n zones : 472\n}\n\n// Convert numbers to words\n// copyright 25th July 2006, by Stephen Chapman http://javascript.about.com\n// permission to use this Javascript on your web page is granted\n// provided that all of the code (including this copyright notice) is\n// used exactly as shown (you can change the numbering system if you wish)\n\n// American Numbering System\nvar th = ['','thousand','million', 'billion','trillion'];\n// uncomment this line for English Number System\n// var th = ['','thousand','million', 'milliard','billion'];\n\nvar dg = ['zero','one','two','three','four','five','six','seven','eight','nine']; \nvar tn = ['ten','eleven','twelve','thirteen','fourteen','fifteen','sixteen','seventeen','eighteen','nineteen']; \nvar tw = ['twenty','thirty','forty','fifty', 'sixty','seventy','eighty','ninety']; \n\nvar calc = function(s)\n{\n s = s.toString(); \n s = s.replace(/[\\, ]/g,''); \n if (s != parseFloat(s)) return 'not a number'; \n var x = s.indexOf('.'); \n if (x == -1) x = s.length; \n if (x > 15) return 'too big'; \n var n = s.split(''); \n var str = ''; var sk = 0; \n for (var i=0; i < x; i++) {\n if ((x-i)%3==2) {\n if (n[i] == '1') {\n str += tn[Number(n[i+1])] + ' '; \n i++; \n sk=1;\n } else if (n[i]!==0) {\n str += tw[n[i]-2] + ' ';\n sk=1;\n }\n } else if (n[i]!==0) {\n str += dg[n[i]] +' '; \n if ((x-i)%3===0) str += 'hundred ';\n sk=1;\n } \n if ((x-i)%3==1) {\n if (sk) str += th[(x-i-1)/3] + ' ';\n sk=0;\n }\n } \n if (x != s.length) {\n var y = s.length; \n str += 'point '; \n for (var i2=x+1; i2<y; i2++) str += dg[n[i]] +' ';\n } \n \n str = str.replace(' ', ' ');\n str = str.trim();\n \n if(str === '') str = 'zero';\n\n return str;\n}\n\nvar convertNumbersToWords = function(message)\n{\n var items = message.toLowerCase().split(' ');\n var newString = '';\n \n for(var i=0; i<items.length; i++) \n {\n if(items[i] == parseFloat(items[i])) //is a number\n {\n var nword = calc(items[i]);\n newString = newString + nword + ' ';\n }\n else\n {\n newString = newString + items[i] + ' ';\n }\n }\n return newString; \n}\n\nvar getWordCodes = function(message) \n{\n var tokens = message.toLowerCase().split(' ');\n var wordCodes = [];\n \n for(var i=0; i<tokens.length; i++) \n {\n if(words[tokens[i]]) //word exists\n {\n var wordString;\n var wordNum = words[tokens[i]];\n if(wordNum < 10) \n {\n wordString = '00' + wordNum.toString();\n } else if(wordNum < 100) \n {\n wordString = '0' + wordNum.toString();\n } else\n {\n wordString = wordNum.toString();\n }\n if(wordString) \n {\n wordCodes[i] = wordString;\n }\n }\n }\n return wordCodes;\n}\n\nmsg.payload = convertNumbersToWords(msg.payload);\nmsg.payload = getWordCodes(msg.payload);//generate array of codes for splitter\n\nreturn msg;","outputs":"1","noerr":0,"x":346.99998474121094,"y":510.99998474121094,"wires":[["667098a9.998f68"]]},{"id":"48c9d0b5.b7363","type":"inject","z":"cddb8bb1.322478","name":"Speak Words","topic":"","payload":"10 intruder in the jacuzzi","payloadType":"str","repeat":"","crontab":"","once":false,"x":114.99998474121094,"y":460.99998474121094,"wires":[["d4e70092.2b19"]]},{"id":"f6f0a8a2.090f58","type":"mqtt in","z":"cddb8bb1.322478","name":"","topic":"ElkM1/speak","broker":"fca9574c.0356a8","x":100.99998474121094,"y":510.99998474121094,"wires":[["d4e70092.2b19"]]},{"id":"c98fb5bb.367048","type":"inject","z":"cddb8bb1.322478","name":"Arm","topic":"","payload":"a1100????","payloadType":"str","repeat":"","crontab":"","once":false,"x":711,"y":669,"wires":[["67c66442.98399c"]]},{"id":"67c66442.98399c","type":"function","z":"cddb8bb1.322478","name":"Format command","func":"if(msg.payload == 'invalid')\n{\n return;\n}\n\nvar writeAscii = function(command) {\n var ascii;\n var len;\n var lenString;\n var cc;\n \n // add the reserved 00 from elk protocol\n command = command + '00';\n \n // calculate the ascii length and prepend the string\n len = command.length + 2;\n lenString = len.toString(16).toUpperCase();\n if(lenString.length == 1) lenString = '0' + lenString;\n ascii = lenString + command;\n \n // get the checksum and append\n cc = calcChecksum(ascii);\n ascii = ascii + cc + '\\r\\n';\n\n return ascii;\n}\n\nvar calcChecksum = function(string) {\n var buf = new Buffer(string);\n\n // Calculate the modulo 256 checksum\n var sum = 0;\n for (var i = 0, l = buf.length; i < l; i++) {\n sum = (sum + buf[i]) % 256;\n }\n // Find the compliment\n sum = 256 - sum;\n\n // Convert to a two byte uppercase hex value\n var chars = sum.toString(16).toUpperCase();\n if (chars.length == 1) chars = '0' + chars;\n return chars;\n}\n\nmsg.payload = writeAscii(msg.payload);\n\nreturn msg;","outputs":"1","noerr":0,"x":1027,"y":708,"wires":[["981ea872.67e158"]]},{"id":"981ea872.67e158","type":"tcp out","z":"cddb8bb1.322478","host":"192.168.2.28","port":"2101","beserver":"client","base64":false,"end":true,"name":"TCP output to ElkM1","x":1090,"y":770,"wires":[]},{"id":"df0c5834.20f3a8","type":"inject","z":"cddb8bb1.322478","name":"Disarm","topic":"","payload":"a0100????","payloadType":"str","repeat":"","crontab":"","once":false,"x":711,"y":714,"wires":[["67c66442.98399c"]]},{"id":"52b35572.ad4cac","type":"inject","z":"cddb8bb1.322478","name":"Real Time Clock","topic":"","payload":"rr","payloadType":"str","repeat":"","crontab":"","once":false,"x":123.99998474121094,"y":709.9999847412109,"wires":[["957b9391.6a847"]]},{"id":"5b1bd3c4.a4e42c","type":"inject","z":"cddb8bb1.322478","name":"Garage Door","topic":"","payload":"ct009","payloadType":"str","repeat":"","crontab":"","once":false,"x":724,"y":620,"wires":[["67c66442.98399c"]]},{"id":"612fb367.9ed04c","type":"inject","z":"cddb8bb1.322478","name":"Internal Siren","topic":"","payload":"cn00100001","payloadType":"str","repeat":"","crontab":"","once":false,"x":758,"y":762,"wires":[["67c66442.98399c"]]},{"id":"8fac00a6.7054","type":"inject","z":"cddb8bb1.322478","name":"Strobe","topic":"","payload":"cn00300002","payloadType":"str","repeat":"","crontab":"","once":false,"x":772,"y":810,"wires":[["67c66442.98399c"]]},{"id":"c43b8524.3bc478","type":"inject","z":"cddb8bb1.322478","name":"External Siren","topic":"","payload":"cn00200002","payloadType":"str","repeat":"","crontab":"","once":false,"x":993,"y":644,"wires":[["67c66442.98399c"]]},{"id":"223c9560.ddc36a","type":"inject","z":"cddb8bb1.322478","name":"Testing","topic":"","payload":"cs","payloadType":"str","repeat":"","crontab":"","once":false,"x":91.99998474121094,"y":824.9999847412109,"wires":[["957b9391.6a847"]]},{"id":"886401e4.779c","type":"inject","z":"cddb8bb1.322478","name":"Speak Time (Phrase)","topic":"","payload":"sp238","payloadType":"str","repeat":"","crontab":"","once":false,"x":992,"y":824,"wires":[["67c66442.98399c"]]},{"id":"f395884e.0c6a78","type":"inject","z":"cddb8bb1.322478","name":"Speak Code","topic":"","payload":"473","payloadType":"str","repeat":"","crontab":"","once":false,"x":118.99998474121094,"y":622.9999694824219,"wires":[["b3f796f8.4c0868"]]},{"id":"7265572.f8d9aa8","type":"mqtt out","z":"cddb8bb1.322478","name":"","topic":"ElkM1/zone4","qos":"","retain":"true","broker":"fca9574c.0356a8","x":553,"y":364,"wires":[]},{"id":"8c7e6025.7381a","type":"mqtt out","z":"cddb8bb1.322478","name":"","topic":"ElkM1/zone1","qos":"","retain":"true","broker":"fca9574c.0356a8","x":552,"y":246,"wires":[]},{"id":"ee84c7b2.117b38","type":"mqtt out","z":"cddb8bb1.322478","name":"","topic":"ElkM1/zone2","qos":"","retain":"true","broker":"fca9574c.0356a8","x":551,"y":289,"wires":[]},{"id":"8cfa65ab.730598","type":"mqtt out","z":"cddb8bb1.322478","name":"","topic":"ElkM1/zone3","qos":"","retain":"true","broker":"fca9574c.0356a8","x":554,"y":327,"wires":[]},{"id":"dd3c380.f22c3c8","type":"mqtt out","z":"cddb8bb1.322478","name":"","topic":"ElkM1/zone5","qos":"","retain":"true","broker":"fca9574c.0356a8","x":551,"y":406,"wires":[]},{"id":"db0e8694.24f178","type":"mqtt out","z":"cddb8bb1.322478","name":"","topic":"ElkM1/zone6","qos":"","retain":"true","broker":"fca9574c.0356a8","x":551.9999847412109,"y":454.99998474121094,"wires":[]},{"id":"bb08d547.44f728","type":"mqtt out","z":"cddb8bb1.322478","name":"","topic":"ElkM1/zone7","qos":"","retain":"true","broker":"fca9574c.0356a8","x":717,"y":230,"wires":[]},{"id":"29b412e1.d64bee","type":"mqtt out","z":"cddb8bb1.322478","name":"","topic":"ElkM1/zone8","qos":"","retain":"true","broker":"fca9574c.0356a8","x":717,"y":280,"wires":[]},{"id":"6ac803d4.9537fc","type":"mqtt out","z":"cddb8bb1.322478","name":"","topic":"ElkM1/zone9","qos":"","retain":"true","broker":"fca9574c.0356a8","x":722,"y":327,"wires":[]},{"id":"549b6576.ab649c","type":"mqtt out","z":"cddb8bb1.322478","name":"","topic":"ElkM1/zone10","qos":"","retain":"true","broker":"fca9574c.0356a8","x":732,"y":365,"wires":[]},{"id":"79fa94cc.86056c","type":"mqtt out","z":"cddb8bb1.322478","name":"","topic":"ElkM1/zone11","qos":"","retain":"true","broker":"fca9574c.0356a8","x":729,"y":404,"wires":[]},{"id":"ce4e566d.31b1a8","type":"mqtt out","z":"cddb8bb1.322478","name":"","topic":"ElkM1/zone12","qos":"","retain":"true","broker":"fca9574c.0356a8","x":730,"y":446,"wires":[]},{"id":"924523b2.6dbae","type":"mqtt out","z":"cddb8bb1.322478","name":"","topic":"ElkM1/zone13","qos":"","retain":"true","broker":"fca9574c.0356a8","x":730,"y":491,"wires":[]},{"id":"5e07ebbf.b7efc4","type":"mqtt in","z":"cddb8bb1.322478","name":"","topic":"ElkM1/command","broker":"fca9574c.0356a8","x":933,"y":528,"wires":[["589cf51b.85f8cc"]]},{"id":"589cf51b.85f8cc","type":"function","z":"cddb8bb1.322478","name":"Convert text to command codes","func":"\nvar commands = {\n garage : 'ct009',\n arm : 'a1100',\n disarm: 'a0100',\n time : 'sp238',\n strobe : 'cn00300002',\n intsiren: 'cn00100001',\n extsiren: 'cn00200002'\n};\n\nvar getCommandCodes = function(message) \n{\n var cmd = message.toLowerCase().split(' ');\n var cmdCode = 'invalid';\n\n if(commands[cmd[0]]) //word exists\n {\n if(cmd[0] == 'arm' || cmd[0] == 'disarm')\n {\n cmdCode = commands[cmd[0]] + cmd[1];\n }\n else\n {\n cmdCode = commands[cmd[0]];\n }\n }\n return cmdCode;\n};\n\nmsg.payload = getCommandCodes(msg.payload);\n\nreturn msg;","outputs":"1","noerr":0,"x":988,"y":595,"wires":[["67c66442.98399c"]]},{"id":"ae9498f6.520008","type":"comment","z":"cddb8bb1.322478","name":"Read Me: Notes (please retain these notes if distributing the flows)","info":"Author: Mike Thompson, GitHub: mike-linus\nDate: 14/8/2016\n\nThe ElkM1 alarm is a very capable alarm system that provides TCP network access via the optional ethernet interface.\n\nElk fortunately documented the command set in their RS-232 Interface Specification ref. ELK-M1_RS232_PROTOCOL\n\nSome excellent Interfaces have been built using node.js ref. https://github.com/kevinohara80/elkington and Perl ref. https://sourceforge.net/projects/elkm1control/ but are missing key components and are not designed to work with node-RED or MQTT.\n\nThe example flow shows how to monitor alarm messages, issue speech commands and other general commands. A continuous speech routine is also included.\n\nTo use, alter the TCP nodes to point to the IP address of your alarm. \n\nNote: The nodes have been configured for use on the internal network using the default non-secure port 2101.","x":263,"y":41,"wires":[]},{"id":"a1fc7a9f.d1e978","type":"delay","z":"cddb8bb1.322478","name":"","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":316.8332824707031,"y":565.0000305175781,"wires":[["b3f796f8.4c0868"]]},{"id":"fca9574c.0356a8","type":"mqtt-broker","z":"","broker":"192.168.2.40","port":"1883","clientid":"node-red","usetls":false,"verifyservercert":true,"compatmode":true,"keepalive":"15","cleansession":false,"willTopic":"","willQos":"0","willRetain":"false","willPayload":"","birthTopic":"","birthQos":"0","birthRetain":"false","birthPayload":""}]