[OBSOLETE] Honeywell / Ademco Vista 20P Integration

Yeah - forever is not setup right… let’s take this offline… I’ll PM you.

Nope… looks like neither the EVL nor the AlarmDecoder support that panel… apparently it’s really old? But it does look like you can easily swap it out for a newer panel. Good luck!

I seem to have lost 2 way communication between my ST and STNP.
If I send command from the app, the alarm panel gets them and works.
However, nothing gets updated on the app side (eg, chime on).
It is also not updating zone status, so if a door is open, is shows closed despite the output on stnp showing the change.
There is also nothing in the live logging.
Any help is greatly appreciated.

My setup is: stnp running on a Pi on 10.0.0.70
ST on 10.0.0.80

root@nodejs:/home/pi/share/smartthings-nodeproxy# nodejs server.js
[2017-06-30T17:30:40.821Z] [stnp] SmartThings Node Proxy listening at http://0.0.0.0:8080
[2017-06-30T17:30:41.046Z] [stnp] Loaded plugin: envisalink
[2017-06-30T17:30:41.080Z] [evl3] Connected to Envisalink at 10.0.0.70:4025
[2017-06-30T17:30:49.020Z] [evl3] {"type":"partition","partition":1,"state":"ready","alpha":"1   DISARMED      Ready to Arm"}
[2017-06-30T17:31:45.316Z] [evl3] {"type":"zone","partition":1,"zone":4,"state":"open"}
[2017-06-30T17:31:45.336Z] [evl3] {"type":"partition","partition":1,"state":"notready","alpha":"FAULT 04 MOTION"}
[2017-06-30T17:31:47.826Z] [evl3] {"type":"zone","partition":1,"zone":"4","state":"closed"}
[2017-06-30T17:31:47.833Z] [evl3] {"type":"partition","partition":1,"state":"ready","alpha":"1   DISARMED      Ready to Arm"}
[2017-06-30T17:31:54.390Z] [stnp] Notify error: Error: socket hang up
[2017-06-30T17:31:54.440Z] [stnp] Notify error: Error: socket hang up
[2017-06-30T17:31:56.966Z] [stnp] Notify error: Error: socket hang up
[2017-06-30T17:31:57.028Z] [stnp] Notify error: Error: socket hang up
^Croot@nodejs:/home/pi/share/smartthings-nodeproxy# ping 10.0.0.80
PING 10.0.0.80 (10.0.0.80) 56(84) bytes of data.
64 bytes from 10.0.0.80: icmp_seq=1 ttl=64 time=2.53 ms
64 bytes from 10.0.0.80: icmp_seq=2 ttl=64 time=1.57 ms

Ok… you definitely have a problem… see

Regarding your setup, I’m guessing that your setup is:
Envisalink on: 10.0.0.70
SmartThings on: 10.0.0.80
STNP on RPi on: ???

Do the following:

  1. Stop STNP and check the following section of your config.json:
 "notify": {
     "address": "192.168.1.12",    // SmartThings Hub IP
     "port": "39500"               // SmartThings Hub HTTP NOTIFY port - default is 39500
 }
  1. Start STNP, launch the Honeywell Security SmartApp and hit “Done”
  2. Check the STNP logs to see what’s going on…
  3. Stop STNP and check the same section of your config.json.
  4. Report back…

You may also want to try and reboot your SmartThings Hub… pull the power and the batteries and then plug it back in…

Just rebooted my hub to no avail.

Envisalink on: 10.0.0.70
SmartThings on: 10.0.0.80
STNP on RPi on: 10.0.0.76

{
  "port": 8080,
  "authCode": "1234",
  "envisalink": {
"address": "10.0.0.70",
"port": "4025",
"password": "user",
"securityCode": "1234",
"dumpZoneTimer": "5",
"panelConfig": {
  "type": "discover",
  "partitions": [
    {
      "partition": 1,
      "name": "Security Panel"
    }
  ],
  "zones": [
    {
      "zone": 1,
      "type": "smoke",
      "name": "AZ 1 Smoke Detector"
    },
    {
      "zone": 2,
      "type": "contact",
      "name": "AZ 2 Front Door"
    },
    {
      "zone": 3,
      "type": "contact",
      "name": "AZ 3 Garage Door"
    },
    {
      "zone": 4,
      "type": "motion",
      "name": "AZ 4 Motion"
    },
    {
      "zone": 5,
      "type": "contact",
      "name": "AZ 5 Kids Rooms"
    },
    {
      "zone": 6,
      "type": "contact",
      "name": "AZ 6 Office"
    },
    {
      "zone": 7,
      "type": "contact",
      "name": "AZ 7 Bedroom"
    },
    {
      "zone": 8,
      "type": "contact",
      "name": "AZ 8 Living Dining Room"
    }
  ]
}
  },
  "dash": {
"buttons": [
  "74:c2:46:a4:90:00"
]
  },
  "notify": {
"address": "10.0.0.80",
"port": "39500"
  }
}

So the notify section in your config.json did not change after you did steps 1-4?

My sincerest apologies! I missed step 2…where I now get “Error saving page” with the following in my live logging:

error physicalgraph.exception.ConflictException: Device still in use. Remove from any SmartApps or Dashboards, then try again @ line 79

Ok - can you update your SmartApp, Device Handlers and envisalink.js plugin from the GitHub repo? And then when you hit “Done” in the SmartApp, make sure “Discover New Devices” is not enabled. Let me know how it goes…

Apps and handlers done.
envisalink.js, um… mine is customized (yes, it did work after customization) because I could not get it to run nodejs on the pi as a service without modifying the paths. :frowning:
I was able to get it to save by disabling “discover zones” in the app and then clicking on done, however that has not resolved the issue.

/**
 *  Envisalink Vista TPI Plugin
 *
 *  Author: redloro@gmail.com
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 *
 *  Many thanks to these guys:
 *   GetVera Plugin: EVL3Vista_4.0.1_EVL/L_EVL3VistaAlarmPanel1.lua
 *   http://forum.eyez-on.com/FORUM/viewtopic.php?f=6&t=301
 *   https://github.com/kholloway/smartthings-dsc-alarm
 *   https://github.com/MattTW/HoneyAlarmServer
 *   https://github.com/oehokie/SmartDSC
 *   https://github.com/oehokie/NodeAlarmProxy
 */
var express = require('express');
var net = require('net');
var app = express();
var nconf = require('nconf');
nconf.file({ file: '/home/pi/share/smartthings-nodeproxy/config.json' });
var notify;
var logger = function(str) {
  mod = 'evl3';
  console.log("[%s] [%s] %s", new Date().toISOString(), mod, str);
}

/**
 * Routes
 */
app.get('/', function (req, res) {
  res.status(200).json({ status: 'Envisalink Vista TPI plugin running' });
});

app.get('/disarm', function (req, res) {
  if (nconf.get('envisalink:securityCode')) {
    evl.command(nconf.get('envisalink:securityCode')+'1');
  }
  res.end();
});

app.get('/armAway', function (req, res) {
  if (nconf.get('envisalink:securityCode')) {
    evl.command(nconf.get('envisalink:securityCode')+'2');
  }
  res.end();
});

app.get('/armStay', function (req, res) {
  if (nconf.get('envisalink:securityCode')) {
    evl.command(nconf.get('envisalink:securityCode')+'3');
  }
  res.end();
});

app.get('/armInstant', function (req, res) {
  if (nconf.get('envisalink:securityCode')) {
    evl.command(nconf.get('envisalink:securityCode')+'7');
  }
  res.end();
});

app.get('/chime', function (req, res) {
  if (nconf.get('envisalink:securityCode')) {
    evl.command(nconf.get('envisalink:securityCode')+'9');
  }
  res.end();
});

app.get('/bypass/:zones', function (req, res) {
  if (nconf.get('envisalink:securityCode')) {
    var zones = req.params.zones.split(',').map(function(x) {
      x = ('00'+x.trim()).slice(-2);
      return (x === '00') ? '' : x;
    }).join('');

    if (zones) {
      evl.command(nconf.get('envisalink:securityCode')+'6'+zones);
    }
  }
  res.end();
});

app.get('/config/:host', function (req, res) {
  var parts = req.params.host.split(":");
  nconf.set('envisalink:address', parts[0]);
  nconf.set('envisalink:port', parts[1]);
  nconf.set('envisalink:password', parts[2]);
  nconf.set('envisalink:securityCode', parts[3]);
  nconf.save(function (err) {
    if (err) {
      logger('Configuration error: '+err.message);
      res.status(500).json({ error: 'Configuration error: '+err.message });
      return;
    }
  });
  res.end();
});

app.get('/discover', function (req, res) {
  evl.discover();
  res.end();
});

app.get('/command/:cmd', function (req, res) {
  //BE CAREFUL
  //evl.command(req.params.cmd);
  res.end();
});

module.exports = function(f) {
  notify = f;
  return app;
};

/**
 * Envisalink
 */
var evl = new Envisalink();
evl.init();

function Envisalink () {
  var self = this;
  var locked = false;
  var panel = {alpha: '', timer: [], partition: 1, zones: []};
  var device = null;
  var deviceRequest = null;
  var deviceResponse = null;
  var responseHandler = function() {};
  var requestHandler = function(a, b, c) {
    deviceRequest = a;
    deviceResponse = b;
    responseHandler = c;
    write(deviceRequest);
  };

  /**
   * init
   */
  this.init = function() {
    if (!nconf.get('envisalink:address') || !nconf.get('envisalink:port') || !nconf.get('envisalink:password')) {
        logger('** NOTICE ** Envisalink settings not set in config file!');
        return;
    }

    if (device && device.writable) { return; }
    if (device) { device.destroy(); }

    device = new net.Socket();
    device.on('error', function(err) {
      logger("Envisalink connection error: "+err.description);
      device.destroy();
      setTimeout(function() { self.init() }, 4000);
    });

    device.on('close', function() {
      logger('Envisalink connection closed.');
      device.destroy();
      setTimeout(function() { self.init() }, 4000);
    });

    device.on('data', function (data) {
      data.toString('utf8').split(/\r?\n/).forEach( function (item) {
          read(item);
      });
    });

    device.connect(nconf.get('envisalink:port'), nconf.get('envisalink:address'), function() {
      logger('Connected to Envisalink at '+nconf.get('envisalink:address')+':'+nconf.get('envisalink:port'));
    });
  };

  // check connection every 60 secs
  setInterval(function() { self.init(); }, 60*1000);

  // experimental: dump zone timers
  var zoneTimer = (nconf.get('envisalink:dumpZoneTimer')) ? parseInt(nconf.get('envisalink:dumpZoneTimer')) : 0;
  if (zoneTimer > 0) {
    setInterval(function() { write('^02,$'); }, 60*1000*zoneTimer);
  }

  /**
   * write
   */
  function write(cmd) {
    if (!device || !device.writable) {
      logger('Envisalink not connected.');
      return;
    }

    if (!cmd || cmd.length == 0) { return; }
    //logger('TX > '+cmd);
    device.write(cmd+'\n');
  }

  this.command = function(cmd) {
    if (locked) { return; }
    write(cmd);
  };

  /**
   * read
   */
  function read(data) {
    if (data.length == 0) { return; }
    //logger('RX < '+data);

    var code = data;
    if (data[0] == '%' || data[0] == '^') {
      code = data.split(',')[0];
      data = data.slice(data.indexOf(',')+1,-1);
    }

    // defined device response handler
    if (responseHandler && deviceResponse) {
      var match = data.indexOf(deviceResponse);
      if (match != -1) {
        responseHandler(data);
      }
    } else {
      // generic handler
      if (RESPONSE_TYPES[code]) {
        responseHandler = RESPONSE_TYPES[code]['handler'];
        responseHandler(data);
      } else {
        logger("Error: ignoring invalid message code from Envisalink: "+code+", data: "+data);
      }
    }
  }

  /**
   * discover
   */
  this.discover = function() {
    if (nconf.get('envisalink:panelConfig')) {
      notify(JSON.stringify(nconf.get('envisalink:panelConfig')));
      logger('Completed panel discovery');
    } else {
      logger('** NOTICE ** Panel configuration not set in config file!');
    }

    //never do auto-discovery
    return;
  };

  /**
   * Generic Handlers
   */
  function login() {
    //logger('Execute login');
    write(nconf.get('envisalink:password'));
  }

  function keypad_update(data) {
    //logger('Execute keypad_update: '+data);

    var map = data.split(',');
    if (map.length != 5 || data.indexOf('%') != -1) {
      logger("Error: ignoring invalid data format from Envisalink: "+data)
      return;
    }

    var msg = {};
    msg.partitionNumber = parseInt(map[0]);
    msg.flags = getLedFlag(map[1]);
    msg.userOrZone = parseInt(map[2]);
    msg.beep = VIRTUAL_KEYPAD_BEEP[map[3]];
    msg.alpha = map[4].trim();
    msg.dscCode = getDscCode(msg.flags);
    //logger(JSON.stringify(msg));
    //logger(JSON.stringify(panel));

    //////////
    // ZONE UPDATE
    //////////

    // all zones are closed
    if (msg.dscCode == 'READY') {
      panel.timer = [];
      for (var n in panel.zones){
        if (panel.zones[n] != 'closed') {
          // notify
          updateZone(msg.partitionNumber, n, 'closed');
        }
      }
    }

    // one or more zones are open
    if (msg.dscCode == '' && !isNaN(msg.userOrZone)) {
      if (panel.zones[msg.userOrZone] != 'open') {
        // reset timer when new zone added
        panel.timer[msg.userOrZone] = 0;
        for (var n in panel.timer) {
          panel.timer[n] = 0;
        }

        // notify
        updateZone(msg.partitionNumber, msg.userOrZone, 'open');
      } else {
        panel.timer[msg.userOrZone]++;

        // experimental: close all zones that have not updated after three ticks
        if (panel.timer[msg.userOrZone] == 2) {
          for (var n in panel.timer) {
            if (panel.timer[n] == 0) {
              // close orphaned zone
              delete panel.timer[n];

              // notify
              updateZone(msg.partitionNumber, n, 'closed');
            } else {
              // reset timer
              panel.timer[n] = 0;
            }
          }
        }
      }
    }

    // zone in alarm
    if (msg.dscCode == 'IN_ALARM' && !isNaN(msg.userOrZone)) {
      if (panel.zones[msg.userOrZone] != 'alarm') {
        // notify
        updateZone(msg.partitionNumber, msg.userOrZone, 'alarm');
      }
    }

    //////////
    // PARTITION UPDATE
    //////////
    if (panel.alpha != msg.alpha) {
      //notify
      updatePartition(msg.partitionNumber, getPartitionState(msg.flags, msg.alpha), msg.alpha);
    }
  }

  function login_success() {
    //logger('Execute login_success');
  }

  function login_failure() {
    //logger('Execute login_failure');
  }

  function login_timeout() {
    //logger('Execute login_timeout');
  }

  function zone_state_change(data) {
    //logger('Execute zone_state_change: '+data);
  }

  function partition_state_change(data) {
    //logger('Execute partition_state_change: '+data);
  }

  function realtime_cid_event(data) {
    //logger('Execute realtime_cid_event: '+data);
  }

  function zone_timer_dump(data) {
    //logger('Execute zone_timer_dump: '+data);
    var queue = [];

    // Swap the couples of every four bytes (little endian to big endian)
    for (var i=0; i<data.length; i+=4) {
      var zoneTimer = data[i+2]+data[i+3]+data[i]+data[i+1];

      var msg = {};
      msg.zoneNumber = (i/4)+1;
      msg.zoneTimer = (parseInt('FFFF', 16) - parseInt(zoneTimer, 16)) * 5;

      // zone timer over 30 secs will be considered closed
      msg.zoneStatus = (msg.zoneTimer < 30) ? 'open' : 'closed';

      // use zone timer dump as backup to check for orphaned zones
      if (msg.zoneStatus == 'closed' &&
          panel.zones[msg.zoneNumber] != 'closed') {
        // notify
        queue.push({
          partition: panel.partition,
          zoneNumber: msg.zoneNumber,
          state: 'closed'
        });
      }
      //logger(JSON.stringify(msg));
    }

    updateThrottler(queue);
  }

  function poll_response(data) {
    //logger('Execute poll_response: '+data);
  }

  function command_response(data) {
    //logger('Execute command_response: '+data);
  }

  /**
   * Helper Functions
   */
  function updateZone(partitionNumber, zoneNumber, state) {
    panel.zones[zoneNumber] = state;

    var msg = JSON.stringify({type: 'zone', partition: partitionNumber, zone: zoneNumber, state: state});
    logger(msg);
    notify(msg);
  }

  function updatePartition(partitionNumber, state, alpha) {
    panel.alpha = alpha;

    var msg = JSON.stringify({type: 'partition', partition: partitionNumber, state: state, alpha: alpha});
    logger(msg);
    notify(msg);
  }

  function updateThrottler(queue) {
    var i = 0;
    while (queue.length) {
      var x = queue.pop();

      // notify
      updateZone(x.partition, x.zoneNumber, x.state);
      i++; if (i == 50) { break; }
    }

    if (!queue.length) { return; }

    setTimeout(function() { updateThrottler(queue) }, 5000);
  }

  function cleanBuffer(data) {
    //trim from response: 13,10
    data = (data[data.length-1]==10) ? data.slice(0,data.length-1) : data;
    data = (data[data.length-1]==13) ? data.slice(0,data.length-1) : data;
    return data.toString('utf8');
  }

  function lpad(val, len) {
    if (len < val.length) return val;

    var pad = "";
    for (var i = 0; i < len-val.length; i++) { pad += "0"; }

    return pad+val;
  }

  function getLedFlag(flag) {
    var flags = {};
    var flagInt = parseInt(flag, 16);
    for (var key in LED_FLAGS) {
      flags[key] = Boolean(LED_FLAGS[key] & flagInt);
    }
    return flags;
  }

  function getDscCode(flags) {
    var dscCode = '';
    if (flags.alarm || flags.alarm_fire_zone || flags.fire) { dscCode = 'IN_ALARM'; }
    else if (flags.system_trouble) { dscCode = 'NOT_READY'; }
    else if (flags.ready) { dscCode = 'READY'; }
    else if (flags.bypass) { dscCode = 'READY_BYPASS'; }
    else if (flags.armed_stay) { dscCode = 'ARMED_STAY'; }
    else if (flags.armed_away) { dscCode = 'ARMED_AWAY'; }
    else if (flags.armed_zero_entry_delay) { dscCode = 'ARMED_MAX'; }
    else if (flags.not_used2 && flags.not_used3) { dscCode = 'NOT_READY'; } // added to handle 'Hit * for faults'
    return dscCode;
  }

  function getPartitionState(flags, alpha) {
    if (flags.alarm || flags.alarm_fire_zone || flags.fire) { return 'alarm'; }
    else if (flags.alarm_in_memory) { return 'alarmcleared'; }
    else if (alpha.indexOf('You may exit now') > 0) { return 'arming'; }
    else if (flags.armed_stay && flags.armed_zero_entry_delay) { return 'armedinstant'; }
    else if (flags.armed_away && flags.armed_zero_entry_delay) { return 'armedmax'; }
    else if (flags.armed_stay) { return 'armedstay'; }
    else if (flags.armed_away) { return 'armedaway'; }
    else if (flags.ready) { return 'ready'; }
    else if (!flags.ready) { return 'notready'; }
    return 'unknown';
  }

  function toTitleCase(str)
  {
    return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
  }

  /**
   * Constants
   */
  var RESPONSE_TYPES = {
    'Login:': {
      'name' : 'Login Prompt',
      'description' : 'Sent During Session Login Only.',
      'handler' : login },
    'OK': {
      'name' : 'Login Success',
      'description' : 'Send During Session Login Only, successful login',
      'handler' : login_success },
    'FAILED' : {
      'name' : 'Login Failure',
      'description' : 'Sent During Session Login Only, password not accepted',
      'handler' : login_failure },
    'Timed Out!' : {
      'name' : 'Login Interaction Timed Out',
      'description' : 'Sent during Session Login Only, socket connection is then closed',
      'handler' : login_timeout },
    '%00' : {
      'name' : 'Virtual Keypad Update',
      'description' : 'The panel wants to update the state of the keypad',
      'handler' : keypad_update },
    '%01' : {
      'name' : 'Zone State Change',
      'description' : 'A zone change-of-state has occurred',
      'handler' : zone_state_change,
      'type' : 'zone'},
    '%02' : {
      'name' : 'Partition State Change',
      'description' : 'A partition change-of-state has occured',
      'handler' : partition_state_change,
      'type' : 'partition' },
    '%03' : {
      'name' : 'Realtime CID Event',
      'description' : 'A system event has happened that is signaled to either the Envisalerts servers or the central monitoring station',
      'handler' : realtime_cid_event,
      'type' : 'system' },
    '%FF' : {
      'name' : 'Envisalink Zone Timer Dump',
      'description' : 'This command contains the raw zone timers used inside the Envisalink. The dump is a 256 character packed HEX string representing 64 UINT16 (little endian) zone timers. Zone timers count down from 0xFFFF (zone is open) to 0x0000 (zone is closed too long ago to remember). Each tick of the zone time is actually 5 seconds so a zone timer of 0xFFFE means 5 seconds ago. Remember, the zone timers are LITTLE ENDIAN so the above example would be transmitted as FEFF.',
      'handler' : zone_timer_dump },
    '^00' : {
      'name': 'Poll',
      'description' : 'Envisalink poll',
      'handler' : poll_response,
      'type' : 'envisalink' },
    '^01' : {
      'name': 'Change Default Partition',
      'description': 'Change the partition which keystrokes are sent to when using the virtual keypad.',
      'handler' : command_response,
      'type' : 'envisalink' },
    '^02' : {
      'name': 'Dump Zone Timers',
      'description' : 'This command contains the raw zone timers used inside the Envisalink. The dump is a 256 character packed HEX string representing 64 UINT16 (little endian) zone timers. Zone timers count down from 0xFFFF (zone is open) to 0x0000 (zone is closed too long ago to remember). Each tick of the zone time is actually 5 seconds so a zone timer of 0xFFFE means 5 seconds ago. Remember, the zone timers are LITTLE ENDIAN so the above example would be transmitted as FEFF.',
      'handler' : command_response,
      'type' : 'envisalink' },
    '^03' : {
      'name': 'Keypress to Specific Partition',
      'description' : 'This will send a keystroke to the panel from an arbitrary partition. Use this if you dont want to change the TPI default partition.',
      'handler' : command_response,
      'type' : 'envisalink' },
    '^0C' : {
      'name': 'Response for Invalid Command',
      'description' : 'This response is returned when an invalid command number is passed to Envisalink',
      'handler': command_response,
      'type' : 'envisalink' }
  };

  var VIRTUAL_KEYPAD_BEEP = {
    '00' : 'off',
    '01' : 'beep 1 time',
    '02' : 'beep 2 times',
    '03' : 'beep 3 times',
    '04' : 'continous fast beep',
    '05' : 'continuous slow beep'
  };

  var LED_FLAGS = {
    "alarm" : 1,
    "alarm_in_memory" : 2,
    "armed_away" : 4,
    "ac_present" : 8,
    "bypass" : 16,
    "chime" : 32,
    "not_used1" : 64,
    "armed_zero_entry_delay" : 128,
    "alarm_fire_zone" : 256,
    "system_trouble" : 512,
    "not_used2" : 1024,
    "not_used3" : 2048,
    "ready" : 4096,
    "fire" : 8192,
    "low_battery" : 16384,
    "armed_stay" : 32768
  };
}

Ok - after you stop STNP any change to the config notify section??

Are you using the Amazon Echo plugin or other plugin?? To see if it’s posting back to the SmartThings hub? And any chance that the SmartThings hub has acquired a new IP address??

Ok.
Stopped.
Changed value in app. Config.json updated.
Only thing in my plugins folder is envisalink.js.
When I hit done in the app, nothing shows up under AT logging.
Same is true for alarm action, for example Disarm. The alarm gets the command from the app, but no logs.
It’s almost like there’s a firewall blocking inbound from the STNP.

Redloro was able to fix my issue with the node.js as an RPI service. Here is the modified /etc/init.d/stnpService file.

#!/bin/sh
#/etc/init.d/stnpService
export PATH=$PATH:/usr/bin:/usr/local/bin
export NODE_PATH=$NODE_PATH:/usr/lib/node_modules:/usr/local/lib/node_modules

case “$1” in
start)

exec forever start --sourceDir=/home/smartthings-nodeproxy -p /home/smartthings-nodeproxy -a -o forever.log -l server.log server.js
;;
stop)
exec forever stop --sourceDir=/home/smartthings-nodeproxy server.js
;;
*)
echo "Usage: /etc/init.d/stnpService {start|stop}"
exit 1
;;
esac
exit 0

Thanks again for the help!!!

Ok… I’ve seen this happen once or twice where the SmartThings Hub goes into a “frozen” state and stops processing inbound requests. This sounds real stupid, but stop STNP and let the Hub sit for 24 hours or so and then fire it all back up. You could try with another Hub, as I’ve also seen these go bad…

Disabling now.
I don’t get the hub issue. If a hub issue, how’s everything else working fine?

Humor me on something…

Should server.js load, and work if there’s nothing in the plugins folder? Not even envisalink.js?
I get this:

root@nodejs:/home/pi/share/smartthings-nodeproxy# nodejs server.js
[2017-06-30T22:02:28.873Z] [stnp] SmartThings Node Proxy listening at http://0.0.0.0:8080
[2017-06-30T22:02:58.079Z] [stnp] 10.0.0.80 GET /subscribe/10.0.0.80:39500
[2017-06-30T22:02:58.309Z] [stnp] 10.0.0.80 GET /plugins/envisalink/config/10.0.0.70:4025:user:????

and here’s my server.js

/**
 *  SmartThings Node Proxy (STNP)
 *
 *  Author: redloro@gmail.com
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 */

////////////////////
// DO NOT CHANGE BELOW THIS LINE
////////////////////
var express = require('express');
var http = require('http');
var app = express();
var nconf = require('nconf');
nconf.file({ file: './config.json' });
var logger = function(str) {
  mod = 'stnp';
  console.log("[%s] [%s] %s", new Date().toISOString(), mod, str);
}

/**
 * Root route
 */
app.get('/', function (req, res) {
  res.status(200).json({ status: 'SmartThings Node Proxy running' });
});

/**
 * Enforce basic authentication route; verify that HTTP.HEADERS['stnp-auth'] == CONFIG['authCode']
 */
app.use(function (req, res, next) {
  logger(req.ip+' '+req.method+' '+req.url);

  var headers = req.headers;
  if (!headers['stnp-auth'] ||
    headers['stnp-auth'] != nconf.get('authCode')) {
    logger('Authentication error');
    res.status(500).json({ error: 'Authentication error' });
    return;
  }

  next();
});

/**
 * Subscribe route used by SmartThings Hub to register for callback/notifications and write to config.json
 * @param {String} host - The SmartThings Hub IP address and port number
 */
app.get('/subscribe/:host', function (req, res) {
  var parts = req.params.host.split(":");
  nconf.set('notify:address', parts[0]);
  nconf.set('notify:port', parts[1]);
  nconf.save(function (err) {
    if (err) {
      logger('Configuration error: '+err.message);
      res.status(500).json({ error: 'Configuration error: '+err.message });
      return;
    }
  });
  res.end();
});

/**
 * Startup
 */
var server = app.listen(nconf.get('port') || 8080, function () {
  logger('SmartThings Node Proxy listening at http://'+server.address().address+':'+server.address().port);
});

/**
 * Load all plugins
 */
var fs = require('fs');
fs.readdir('/home/pi/share/smartthings-nodeproxy/plugins', function(err, files) {
  if (!err) {
    files
    .filter(function(file) { return file.substr(-3) === '.js'; })
    .forEach(function(file) {
      var plugin = file.split(".")[0];
      app.use('/plugins/'+plugin, require('/home/pi/share/smartthings-nodeproxy/plugins/'+plugin)(function(data){notify(plugin,data);}));
      logger('Loaded plugin: '+plugin);
    });
  } else {
    logger(err);
  }
});

/**
 * Callback to the SmartThings Hub via HTTP NOTIFY
 * @param {String} plugin - The name of the STNP plugin
 * @param {String} data - The HTTP message body
 */
var notify = function(plugin, data) {
  if (!nconf.get('notify:address') || nconf.get('notify:address').length == 0 ||
    !nconf.get('notify:port') || nconf.get('notify:port') == 0) {
    logger("Notify server address and port not set!");
    return;
  }

  var opts = {
    method: 'NOTIFY',
    host: nconf.get('notify:address'),
    port: nconf.get('notify:port'),
    path: '/notify',
    headers: {
      'CONTENT-TYPE': 'application/json',
      'CONTENT-LENGTH': Buffer.byteLength(data),
      'stnp-plugin': plugin
    }
  };

  var req = http.request(opts);
  req.on('error', function(err, req, res) {
    logger("Notify error: "+err);
  });
  req.write(data);
  req.end();
}

Ok, I feel like I’m getting close but I’m not quite there yet. I have my pi connected to my EVL-3, the EVL-3 is connected to the internet, and the Eyes-On website can see my device. When I try “npm run start”, I get the following:

> smartthings-nodeproxy@1.0.0 start /home/pi
> node server.js

events.js:160
      throw er; // Unhandled 'error' event
      ^

Error: listen EADDRINUSE :::8080
    at Object.exports._errnoException (util.js:1018:11)
    at exports._exceptionWithHostPort (util.js:1041:20)
    at Server._listen2 (net.js:1258:14)
    at listen (net.js:1294:10)
    at Server.listen (net.js:1390:5)
    at EventEmitter.listen (/home/pi/node_modules/express/lib/application:24)
    at Object.<anonymous> (/home/pi/server.js:74:18)
    at Module._compile (module.js:570:32)
    at Object.Module._extensions..js (module.js:579:10)
    at Module.load (module.js:487:32)

npm ERR! Linux 4.9.34-v7+
npm ERR! argv "/usr/bin/nodejs" "/usr/bin/npm" "run" "start"
npm ERR! node v6.11.0
npm ERR! npm  v3.10.10
npm ERR! code ELIFECYCLE
npm ERR! smartthings-nodeproxy@1.0.0 start: `node server.js`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the smartthings-nodeproxy@1.0.0 start script 'node ser.
npm ERR! Make sure you have the latest version of node.js and npm install
npm ERR! If you do, this is most likely a problem with the smartthings-no package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR!     node server.js
npm ERR! You can get information on how to open an issue for this project
npm ERR!     npm bugs smartthings-nodeproxy
npm ERR! Or if that isn't available, you can get their info via:
npm ERR!     npm owner ls smartthings-nodeproxy
npm ERR! There is likely additional logging output above.

npm ERR! Please include the following file with any support request:
npm ERR!     /home/pi/npm-debug.log

Here’s my log file output:

0 info it worked if it ends with ok
1 verbose cli [ '/usr/bin/nodejs', '/usr/bin/npm', 'run', 'start' ]
2 info using npm@3.10.10
3 info using node@v6.11.0
4 verbose config Skipping project config: /home/pi/.npmrc. (matches userconfig)
5 verbose run-script [ 'prestart', 'start', 'poststart' ]
6 info lifecycle smartthings-nodeproxy@1.0.0~prestart: smartthings-nodeproxy@1.0.0
7 silly lifecycle smartthings-nodeproxy@1.0.0~prestart: no script for prestart, continuing
8 info lifecycle smartthings-nodeproxy@1.0.0~start: smartthings-nodeproxy@1.0.0
9 verbose lifecycle smartthings-nodeproxy@1.0.0~start: unsafe-perm in lifecycle true
10 verbose lifecycle smartthings-nodeproxy@1.0.0~start: PATH: /usr/lib/node_modules/npm/bin/node-gyp-bin:/home/pi/node_modules/.bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games
11 verbose lifecycle smartthings-nodeproxy@1.0.0~start: CWD: /home/pi
12 silly lifecycle smartthings-nodeproxy@1.0.0~start: Args: [ '-c', 'node server.js' ]
13 silly lifecycle smartthings-nodeproxy@1.0.0~start: Returned: code: 1  signal: null
14 info lifecycle smartthings-nodeproxy@1.0.0~start: Failed to exec start script
15 verbose stack Error: smartthings-nodeproxy@1.0.0 start: `node server.js`
15 verbose stack Exit status 1
15 verbose stack     at EventEmitter.<anonymous> (/usr/lib/node_modules/npm/lib/utils/lifecycle.js:255:16)
15 verbose stack     at emitTwo (events.js:106:13)
15 verbose stack     at EventEmitter.emit (events.js:191:7)
15 verbose stack     at ChildProcess.<anonymous> (/usr/lib/node_modules/npm/lib/utils/spawn.js:40:14)
15 verbose stack     at emitTwo (events.js:106:13)
15 verbose stack     at ChildProcess.emit (events.js:191:7)
15 verbose stack     at maybeClose (internal/child_process.js:891:16)
15 verbose stack     at Process.ChildProcess._handle.onexit (internal/child_process.js:226:5)
16 verbose pkgid smartthings-nodeproxy@1.0.0
17 verbose cwd /home/pi
18 error Linux 4.9.34-v7+
19 error argv "/usr/bin/nodejs" "/usr/bin/npm" "run" "start"
20 error node v6.11.0
21 error npm  v3.10.10
22 error code ELIFECYCLE
23 error smartthings-nodeproxy@1.0.0 start: `node server.js`
23 error Exit status 1
24 error Failed at the smartthings-nodeproxy@1.0.0 start script 'node server.js'.
24 error Make sure you have the latest version of node.js and npm installed.
24 error If you do, this is most likely a problem with the smartthings-nodeproxy package,
24 error not with npm itself.
24 error Tell the author that this fails on your system:
24 error     node server.js
24 error You can get information on how to open an issue for this project with:
24 error     npm bugs smartthings-nodeproxy
24 error Or if that isn't available, you can get their info via:
24 error     npm owner ls smartthings-nodeproxy
24 error There is likely additional logging output above.
25 verbose exit [ 1, true ]

And here’s my config.json. I don’t have the zones totally set up yet, I just copy/pasted someone else’s config file from above and changed the ip address, to try to get at least that part working.

{
"envisalink": {
"address": "172.24.1.50",
"port": "4025",
"password": "user",
"securityCode": "1234",
"panelConfig": {
"type": "discover",
"partitions": [
{
"partition": 1,
"name": "Security Panel"
}
],
"zones": [
{
"zone": 9,
"type": "contact",
"name": "Front Door"
},
{
"zone": 10,
"type": "contact",
"name": "Back Door"
},
{
"zone": 11,
"type": "contact",
"name": "Back Door Window"
},
{
"zone": 12,
"type": "contact",
"name": "Dining Room Window"
},
{
"zone": 13,
"type": "contact",
"name": "Master Bedroom Window Right"
},
{
"zone": 14,
"type": "contact",
"name": "Master Bedroom Window Left"
},
{
"zone": 15,
"type": "contact",
"name": "Living Room Sliding Glass Door"
},
{
"zone": 16,
"type": "contact",
"name": "Family Room Sliding Glass Door"
},
{
"zone": 17,
"type": "motion",
"name": "Family Room Motion"
},
{
"zone": 18,
"type": "contact",
"name": "D Bathroom Window"
},
{
"zone": 19,
"type": "contact",
"name": "D Bedroom Window Right"
},
{
"zone": 20,
"type": "contact",
"name": "D Bedroom Window Left"
},
{
"zone": 21,
"type": "contact",
"name": "Z Bedroom Window Right"
},
{
"zone": 22,
"type": "contact",
"name": "Z Bedroom Window Left"
},
{
"zone": 23,
"type": "contact",
"name": "X Bedroom Window Right"
},
{
"zone": 24,
"type": "contact",
"name": "X Bedroom Window Left"
},
{
"zone": 25,
"type": "motion",
"name": "Front Door Motion"
},
{
"zone": 26,
"type": "contact",
"name": "Garage Door"
}
]
}
},
"notify": {
"address": "192.168.1.1",
"port": "39500"
}
}

It’s all tied to specific feature sets… sometimes certain features or functions work and others don’t… problem areas that come to mind:

  1. SmartThings Home Monitor (not officially supported for ST community)
  2. SSDP messages and notification (not officially supported for ST community → seems to be the problem you’re having)
  3. Timers and events

That’s correct… and why you see these messages…

[2017-06-30T22:02:28.873Z] [stnp] SmartThings Node Proxy listening at http://0.0.0.0:8080
  • Service starting up…
[2017-06-30T22:02:58.079Z] [stnp] 10.0.0.80 GET /subscribe/10.0.0.80:39500
  • Service received a subscribe message from a SmartApp… assume you hit “Done” in a SmartApp
[2017-06-30T22:02:58.309Z] [stnp] 10.0.0.80 GET /plugins/envisalink/config/10.0.0.70:4025:user:????
  • Service received config message for Envisalink plugin… assume you hit “Done” in the Honeywell Security SmartApp

But nothing is going to happen as there is no plug-in running.

Did you do Step #12 under FAQ -> What do I need to do to get this running? in the first post in this thread?