Elk M1 Gold Alarm System TCP Interface

Author: Mike Thompson, GitHub: mike-linus Date: 14/8/2016

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=-1;\n}  \nelse\n{\n    boostate=0;\n}\n\nmsg.topic = alarminfo[0];\n//msg.topic = alarminfo[0] + ' ' + alarminfo[1];//ensures zone and state used for queue\n//txtstate = alarminfo[1] + ' ' + msg.timestamp;\n\n//msg.payload = {\"state\":boostate,\"txtstate\":txtstate};\nmsg.payload = boostate;\n\nreturn msg;","outputs":"1","noerr":0,"x":170,"y":188,"wires":[["a4145a1e.5beba8","d2e3d5be.9c5998"]]},{"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","rules":[{"t":"eq","v":"ZC"},{"t":"eq","v":"AS"}],"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":143,"y":727,"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":342,"y":682,"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":447,"y":745,"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":364,"y":811,"wires":[["bd35d5d8.42ca28"]]},{"id":"bd35d5d8.42ca28","type":"debug","z":"cddb8bb1.322478","name":"","active":true,"console":"false","complete":"false","x":578,"y":814,"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":340,"y":546,"wires":[["8cd545cb.732ab8"]]},{"id":"667098a9.998f68","type":"splitter","z":"cddb8bb1.322478","name":"","property":"payload","x":149,"y":546,"wires":[["b3f796f8.4c0868"]]},{"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":458,"y":604,"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":347,"y":489,"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":115,"y":439,"wires":[["d4e70092.2b19"]]},{"id":"f6f0a8a2.090f58","type":"mqtt in","z":"cddb8bb1.322478","name":"","topic":"ElkM1/speak","broker":"fca9574c.0356a8","x":101,"y":489,"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":124,"y":670,"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":90,"y":775,"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":119,"y":591,"wires":[["b3f796f8.4c0868"]]},{"id":"7265572.f8d9aa8","type":"mqtt out","z":"cddb8bb1.322478","name":"","topic":"ElkM1/zone4","qos":"","retain":"false","broker":"fca9574c.0356a8","x":553,"y":364,"wires":[]},{"id":"8c7e6025.7381a","type":"mqtt out","z":"cddb8bb1.322478","name":"","topic":"ElkM1/zone1","qos":"","retain":"false","broker":"fca9574c.0356a8","x":552,"y":246,"wires":[]},{"id":"ee84c7b2.117b38","type":"mqtt out","z":"cddb8bb1.322478","name":"","topic":"ElkM1/zone2","qos":"","retain":"false","broker":"fca9574c.0356a8","x":551,"y":289,"wires":[]},{"id":"8cfa65ab.730598","type":"mqtt out","z":"cddb8bb1.322478","name":"","topic":"ElkM1/zone3","qos":"","retain":"false","broker":"fca9574c.0356a8","x":554,"y":327,"wires":[]},{"id":"dd3c380.f22c3c8","type":"mqtt out","z":"cddb8bb1.322478","name":"","topic":"ElkM1/zone5","qos":"","retain":"false","broker":"fca9574c.0356a8","x":551,"y":406,"wires":[]},{"id":"db0e8694.24f178","type":"mqtt out","z":"cddb8bb1.322478","name":"","topic":"ElkM1/zone6","qos":"","retain":"false","broker":"fca9574c.0356a8","x":556,"y":447,"wires":[]},{"id":"bb08d547.44f728","type":"mqtt out","z":"cddb8bb1.322478","name":"","topic":"ElkM1/zone7","qos":"","retain":"false","broker":"fca9574c.0356a8","x":717,"y":230,"wires":[]},{"id":"29b412e1.d64bee","type":"mqtt out","z":"cddb8bb1.322478","name":"","topic":"ElkM1/zone8","qos":"","retain":"false","broker":"fca9574c.0356a8","x":717,"y":280,"wires":[]},{"id":"6ac803d4.9537fc","type":"mqtt out","z":"cddb8bb1.322478","name":"","topic":"ElkM1/zone9","qos":"","retain":"false","broker":"fca9574c.0356a8","x":722,"y":327,"wires":[]},{"id":"549b6576.ab649c","type":"mqtt out","z":"cddb8bb1.322478","name":"","topic":"ElkM1/zone10","qos":"","retain":"false","broker":"fca9574c.0356a8","x":732,"y":365,"wires":[]},{"id":"79fa94cc.86056c","type":"mqtt out","z":"cddb8bb1.322478","name":"","topic":"ElkM1/zone11","qos":"","retain":"false","broker":"fca9574c.0356a8","x":729,"y":404,"wires":[]},{"id":"ce4e566d.31b1a8","type":"mqtt out","z":"cddb8bb1.322478","name":"","topic":"ElkM1/zone12","qos":"","retain":"false","broker":"fca9574c.0356a8","x":730,"y":446,"wires":[]},{"id":"924523b2.6dbae","type":"mqtt out","z":"cddb8bb1.322478","name":"","topic":"ElkM1/zone13","qos":"","retain":"false","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":"d2e3d5be.9c5998","type":"debug","z":"cddb8bb1.322478","name":"","active":false,"console":"false","complete":"false","x":420,"y":175,"wires":[]},{"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":"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":""}]
mike-linus

Flow Info

created 1 year, 5 months ago

Node Types

Core
  • comment (x1)
  • debug (x2)
  • function (x10)
  • inject (x12)
  • mqtt in (x2)
  • mqtt out (x15)
  • mqtt-broker (x1)
  • switch (x2)
  • tcp in (x1)
  • tcp out (x2)
  • tcp request (x1)
Other

Tags

  • ElkM1
  • Alarm
  • Elk
  • M1
  • RS232
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option