Scheduled Polling Events - auto closing garage door [SOLUTION]

Started by n2ppp, August 04, 2016, 12:52:29 AM

Felix

Syntax errors is a different can of worms.
When you make metrics.js changes, you need to restart the app for it to take effect.
If there are syntax errors that might cause your app to not be able to start.

n2ppp

Felix,
  I'm happy to report that my late-night garage door auto-close action is now working like a champ.  8)

Thanks again,
Al
Alex

Felix

Yay, great!  8)
Just be careful with auto closing garage doors  ;)

n2ppp

Of course!  That's why I'm limiting it to late at night and delaying the closure for 30 minutes.  Next, I plan to integrate an SMS warning notification so that there will be an opportunity to disable the auto-close if desired.

Thanks,
Al
Alex

Nspector5

I am looking at doing something similar. I have a temperature sensor in my deep freezer and I want to get a warning message (text) if the temperature rises too high, but I don't want to get an additional message for about an hour if there is still a problem. Could you attach the complete working code? Thanks.

Felix

@Nspector5:
Here's a working example for a motion sensor. It simply verifies if the value == "MOTION" and checks a timestamp variable called lastSMS (if it exists and when it occured). If that value is more than 1hour ago (in milliseconds) then it will send the SMS and save the timestamp of the sent SMS back into that variable and persists it to the node database.

  motionSMSLimiter : { label:'Motion : SMS Limited', icon:'comment', descr:'Send SMS when MOTION is detected, once per hour', 
    serverExecute:function(node) { 
      if (node.metrics['M'] && node.metrics['M'].value == 'MOTION' && (Date.now() - new Date(node.metrics['M'].updated).getTime() < 2000)) /*check if M metric exists and value is MOTION, received less than 2s ago*/
      {
        var approveSMS = false;
        if (node.metrics['M'].lastSMS) /*check if lastSMS value is not NULL ... */
        {
          if (Date.now() - node.metrics['M'].lastSMS > 3600000) /*check if lastSMS timestamp is more than 1hr ago*/
          {
            approveSMS = true;
          }
        }
        else
        {
          approveSMS = true;
        }
        
        if (approveSMS)
        {
          node.metrics['M'].lastSMS = Date.now();
          sendSMS('MOTION DETECTED', 'MOTION WAS DETECTED ON NODE: [' + node._id + ':' + node.label + '] @ ' + (new Date().toLocaleTimeString() + (new Date().getHours() > 12 ? 'PM':'AM')));
          db.update({ _id: node._id }, { $set : node}, {}, function (err, numReplaced) { console.log('   ['+node._id+'] DB-Updates:' + numReplaced);}); /*save lastSMS timestamp to DB*/
        }
        else console.log('   ['+node._id+'] MOTION SMS skipped.');
      };
    }
  },


If you look in gateway.db then a new variable will be saved that holds the last SMS sent timestamp:

{"_id":189,
  "updated":1473347821403,
  "type":"MotionMote",
  "label":"Motion BASEMENT",
  "rssi":76,
  "metrics":{
    "START":{"label":"START","value":"Started","updated":1472247757850},
    "M":{"label":"M","value":"MOTION","updated":1473347821403,"pin":1,"graph":1,"lastSMS":1473347821416},
    "V":{"label":"V","value":3.9,"unit":"v","updated":1473347821403,"pin":0},
    "F":{"label":"Temperature","value":72.45,"unit":"°","updated":1473347821403,"pin":1,"graph":1}
  },
  "events":{"motionAlert":0,"motionSMSLimiter":1}
}

To make this work for a numeric value you would simply have to check if your node.metric.value is greater than your desired threshold.
For instance the above MotionMote has a BME280 sensor that produces an F temperature metric.
I could create another event for this to send an SMS when this metric exceeds 75°F, something like this:

  temperatureSMSLimiter : { label:'THAlert : SMS Limited', icon:'comment', descr:'Send SMS when F>75°, once per hour', 
    serverExecute:function(node) { 
      if (node.metrics['F'] && node.metrics['F'].value > 75 && (Date.now() - new Date(node.metrics['F'].updated).getTime() < 2000)) /*check if M metric exists and value is MOTION, received less than 2s ago*/
      {
        var approveSMS = false;
        if (node.metrics['F'].lastSMS) /*check if lastSMS value is not NULL ... */
        {
          if (Date.now() - node.metrics['F'].lastSMS > 3600000) /*check if lastSMS timestamp is more than 1hr ago*/
          {
            approveSMS = true;
          }
        }
        else
        {
          approveSMS = true;
        }
        
        if (approveSMS)
        {
          node.metrics['F'].lastSMS = Date.now();
          sendSMS('Temperature > 75° !', 'Temperature alert (>75°F!): [' + node._id + ':' + node.label + '] @ ' + (new Date().toLocaleTimeString() + (new Date().getHours() > 12 ? 'PM':'AM')));
          db.update({ _id: node._id }, { $set : node}, {}, function (err, numReplaced) { console.log('   ['+node._id+'] DB-Updates:' + numReplaced);}); /*save lastSMS timestamp to DB*/
        }
        else console.log('   ['+node._id+'] THAlert SMS skipped.');
      };
    }
  },


Let me know if this works for you. Update your metrics.js with this new event (adjust the temperature comparison value/condition), then restart your gateway from the settings page, then go to your node and activate this event (via "Add event" button). Then your

Nspector5

Thank you. This works. I also slightly modified and added support for sending SMS alerts once per day when one of my nodes gets a low battery voltage:

lowbattAAA : { label:'Low Battery AAA', icon:'comment', descr:'Send SMS when Battery below 3.40V, once per day', 
    serverExecute:function(node) { 
      if (node.metrics['Battery'] && node.metrics['Battery'].value < 340 && (Date.now() - new Date(node.metrics['Battery'].updated).getTime() < 2000)) /*check if Battery metric exists and value was received less than 2s ago*/
      {
        var approveSMS = false;
        if (node.metrics['Battery'].lastSMS) /*check if lastSMS value is not NULL ... */
        {
          if (Date.now() - node.metrics['Battery'].lastSMS > 86400000) /*check if lastSMS timestamp is more than 1 day ago*/
          {
            approveSMS = true;
          }
        }
        else
        {
          approveSMS = true;
        }
        
        if (approveSMS)
        {
          node.metrics['Battery'].lastSMS = Date.now();
          sendSMS('Low Battery', 'Low Battery Alert!: [' + node._id + ':' + node.label + '] @ ' + (new Date().toLocaleTimeString() + (new Date().getHours() > 12 ? 'PM':'AM')));
          db.update({ _id: node._id }, { $set : node}, {}, function (err, numReplaced) { console.log('   ['+node._id+'] DB-Updates:' + numReplaced);}); /*save lastSMS timestamp to DB*/
        }
        else console.log('   ['+node._id+'] Low Battery AAA SMS skipped.');
      };
    }
  },

Felix

Awesome.

Other than writing some javascript, does this type of thing feel scary to attempt?

Please say no :)
I think it should be not since javascript is awesome.
I can't even begin to explain what a pain this would be to do in something like .net or C#.

Nspector5

No, it is not scary to attempt; but to that end, I'm more comfortable with ANSI C and AVR assembly than with most higher level languages, so it takes some getting used to. Also, I was (and to a large extent, still am) still very unfamiliar with node.js and how it is supposed to work. Thank you for your willingness to help.