Skip to content

Commit 15f2eba

Browse files
committed
9.1 main commit
1 parent 64fd143 commit 15f2eba

19 files changed

Lines changed: 1706 additions & 1065 deletions

gateway.js

Lines changed: 404 additions & 124 deletions
Large diffs are not rendered by default.

metrics.js

Lines changed: 0 additions & 729 deletions
This file was deleted.

metrics/_LowPowerLab/RadioThermostat_CT50.js

Lines changed: 299 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//Door Bell Mote - https://lowpowerlab.com/shop/product/132
2+
3+
exports.metrics = {
4+
ring : { name:'RING', regexp:/\bRING\b/i, value:'RING', pin:1, graph:1, logValue:1, graphValSuffix:'!', graphOptions:{ legendLbl:'Doorbell rings', lines: { show:false, fill:false }, points: { show:true, radius:8, fill:false }, grid: { backgroundColor: {colors:['#000', '#a40']}}, yaxis: { ticks: 0 }}},
5+
BELL_DISABLED : { name:'Status', regexp:/\bBELL\:0\b/i, value:'OFF'},
6+
BELL_ENABLED : { name:'Status', regexp:/\bBELL\:1\b/i, value:'ON'},
7+
}
8+
9+
exports.events = {
10+
doorbellSound : { label:'Doorbell : Sound', icon:'audio', descr:'Play sound when doorbell rings', serverExecute:function(node) { if (node.metrics['RING'] && node.metrics['RING'].value == 'RING' && (Date.now() - new Date(node.metrics['RING'].updated).getTime() < 2000)) { io.sockets.emit('PLAYSOUND', 'sounds/doorbell.wav'); }; } },
11+
doorbellSMS : { label:'Doorbell : SMS', icon:'comment', descr:'Send SMS when Doorbell button is pressed', serverExecute:function(node) { if (node.metrics['RING'] && node.metrics['RING'].value == 'RING' && (Date.now() - new Date(node.metrics['RING'].updated).getTime() < 2000)) { sendSMS('DOORBELL', 'DOORBELL WAS RINGED: [' + node._id + '] ' + node.label.replace(/\{.+\}/ig, '') + ' @ ' + new Date().toLocaleTimeString()); }; } },
12+
13+
doorbellSnapshot : { label:'Doorbell : Snapshot', icon:'camera', descr:'Send IPCamera snapshot when Doorbell is pressed pressed',
14+
serverExecute:function(node) {
15+
if (node.metrics['RING'] && node.metrics['RING'].value == 'RING' && (Date.now() - new Date(node.metrics['RING'].updated).getTime() < 2000))
16+
{
17+
db.findOne({ _id : node._id }, function (err, dbNode) {
18+
try {
19+
var snapshotURL = dbNode.settings && dbNode.settings.ipcam_snapURL ? dbNode.settings.ipcam_snapURL : (metricsDef.motes[dbNode.type].settings.ipcam_snapURL || settings.misc.ipcam_snapURL.value);
20+
sendEmail('Someone at the door!', 'Someone rang the doorbell! @ ' + new Date().toLocaleTimeString(), [{path: snapshotURL, filename: 'snapshot.jpg'}]);
21+
} catch(ex) {
22+
console.error('Event doorbellSnapshot FAIL: dbNode=' + JSON.stringify(dbNode));
23+
console.error(ex.message);
24+
}
25+
});
26+
}
27+
}
28+
},
29+
}
30+
31+
exports.motes = {
32+
DoorBellMote: {
33+
label : 'DoorBell',
34+
icon : 'icon_doorbell.png',
35+
settings: { ipcam_snapURL: '' },
36+
controls : { ring : { states: [{ label:'Ring it!', action:'RING', icon:'audio' }]},
37+
status : { states: [{ label:'Disabled', action:'BELL:1', css:'background-color:#FF9B9B;', icon:'fa-bell-slash', condition:''+function(node) { return node.metrics['Status']!=null && node.metrics['Status'].value == 'OFF'; }},
38+
{ label:'Enabled', action:'BELL:0', css:'background-color:#9BFFBE;color:#000000', icon:'fa-bell', condition:''+function(node) { return node.metrics['Status']==null || node.metrics['Status'].value == 'ON'; }}]},
39+
},
40+
},
41+
}

metrics/_LowPowerLab/garagemote.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//GarageMote - https://lowpowerlab.com/garagemote/
2+
3+
exports.metrics = {
4+
//NOTE the \b word boundary is used to avoid matching "OPENING" (ie OPEN must be followed by word boundary/end of word)
5+
open : { name:'Status', regexp:/(?:STS\:)?(OPN|OPEN)\b/i, value:'OPEN', pin:1, graph:1, logValue:2, graphOptions:{ legendLbl:'Garage door events', yaxis: {ticks:0}, colors:['#4a0'], /*lines: { lineWidth:1 }*/}},
6+
opening : { name:'Status', regexp:/(?:STS\:)?(OPNING|OPENING)/i, value:'OPENING..', pin:1, graph:1, logValue:1 },
7+
closed : { name:'Status', regexp:/(?:STS\:)?(CLS|CLOSED)/i, value:'CLOSED', pin:1, graphValPrefix:' Door: ', graph:1, logValue:0 },
8+
closing : { name:'Status', regexp:/(?:STS\:)?(CLSING|CLOSING)/i, value:'CLOSING..', pin:1, graph:1, logValue:1.1 }, //1.1 to avoid a match with "OPENING"
9+
unknown : { name:'Status', regexp:/(?:STS\:)?(UNK|UNKNOWN)/i, value:'UNKNOWN!', pin:1, graph:1, logValue:0.5 },
10+
}
11+
12+
exports.events = {
13+
garageSMS : { label:'Garage : SMS', icon:'comment', descr:'Send SMS when garage is OPENING', serverExecute:function(node) { if (node.metrics['Status'] && (node.metrics['Status'].value.indexOf('OPENING')>-1) && (Date.now() - new Date(node.metrics['Status'].updated).getTime() < 2000)) { sendSMS('Garage event', 'Garage was opening on node : [' + node._id + ':' + node.label.replace(/\{.+\}/ig, '') + '] @ ' + new Date().toLocaleTimeString()); }; } },
14+
garagePoll: { label:'Garage : POLL Status', icon:'comment', descr:'Poll Garage Status', nextSchedule:function(nodeAtScheduleTime) { return 30000; }, scheduledExecute:function(nodeAtScheduleTime) { db.findOne({ _id : nodeAtScheduleTime._id }, function (err, nodeRightNow) { if (nodeRightNow) { /*just emit a log the status to client(s)*/ io.sockets.emit('LOG', 'GARAGE POLL STATUS: ' + nodeRightNow.metrics['Status'].value ); } }); } },
15+
16+
garageSnapshotEmail : { label:'Garage : Snapshot', icon:'camera', descr:'Send IPCam snapshot when garage is OPENING',
17+
serverExecute: function(node) {
18+
if (node.metrics['Status'] && (node.metrics['Status'].value.indexOf('OPENING')>-1) && (Date.now() - new Date(node.metrics['Status'].updated).getTime() < 2000))
19+
{
20+
db.findOne({ _id : node._id }, function (err, dbNode) {
21+
var snapshotURL = dbNode.settings && dbNode.settings.ipcam_snapURL ? dbNode.settings.ipcam_snapURL : (metricsDef.motes[dbNode.type].settings.ipcam_snapURL || settings.misc.ipcam_snapURL.value);
22+
sendEmail('GARAGE OPENING', 'GARAGE IS OPENING: [' + dbNode._id + ':' + dbNode.label.replace(/\{.+\}/ig, '') + '] @ ' + new Date().toLocaleTimeString(), [{path: snapshotURL, filename: 'snapshot.jpg'}]);
23+
});
24+
}
25+
}
26+
},
27+
}
28+
29+
exports.motes = {
30+
GarageMote : {
31+
label : 'Garage Opener',
32+
icon : 'icon_garage.png',
33+
settings : { ipcam_snapURL: '' },
34+
controls : { refresh : { states: [{ label:'Refresh', action:'STS', icon:'refresh' }]},
35+
opencls : { states: [{ label:'Open!', action:'OPN', icon:'arrow-u', css:'background-color:#FF9B9B;', condition:''+function(node) { return node.metrics['Status']!=null && node.metrics['Status'].value == 'CLOSED';}},
36+
{ label:'Opening..', action:'', icon:'forbidden', css:'background-color:#FFF000;', condition:''+function(node) { return node.metrics['Status']!=null && node.metrics['Status'].value == 'OPENING';}},
37+
{ label:'Close!', action:'CLS', icon:'arrow-d', css:'background-color:#9BFFBE;color:#000000', condition:''+function(node) { return node.metrics['Status']!=null && node.metrics['Status'].value == 'OPEN';}},
38+
{ label:'Closing..', action:'', icon:'forbidden', css:'background-color:#FFF000;', condition:''+function(node) { return node.metrics['Status']!=null && node.metrics['Status'].value == 'CLOSING';}}]
39+
}
40+
}
41+
},
42+
}

metrics/_LowPowerLab/motionmote.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//MotionMote and Mailbox notifier - https://lowpowerlab.com/motion
2+
3+
exports.metrics = {
4+
motion : { name:'M', regexp:/\bMOTION\b/i, value:'MOTION', pin:1, graph:1, logValue:1, graphValSuffix:' detected!', graphOptions:{ legendLbl:'Motion', lines: { show:false, fill:false }, points: { show:true, radius:8, fill:false }, grid: { backgroundColor: {colors:['#000', '#03c', '#08c']}}, yaxis: { ticks: 0 }}},
5+
lastMotion : { name:'LO', regexp:/(?:LO|LM)\:((?:\d+h)?\d{1,2}m|\d{1,2}s)/i, value:'', pin:1 },
6+
}
7+
8+
exports.events = {
9+
motionAlert : { label:'Motion : Alert', icon:'audio', descr:'Alert sound when MOTION is detected', serverExecute:function(node) { if (node.metrics['M'] && node.metrics['M'].value == 'MOTION' && (Date.now() - new Date(node.metrics['M'].updated).getTime() < 2000)) { io.sockets.emit('PLAYSOUND', 'sounds/alert.wav'); }; } },
10+
mailboxAlert : { label:'Mailbox Open Alert!', icon:'audio', descr:'Message sound when mailbox is opened', serverExecute:function(node) { if (node.metrics['M'] && node.metrics['M'].value == 'MOTION' && (Date.now() - new Date(node.metrics['M'].updated).getTime() < 2000)) { io.sockets.emit('PLAYSOUND', 'sounds/incomingmessage.wav'); }; } },
11+
motionEmail : { label:'Motion : Email', icon:'mail', descr:'Send email when MOTION is detected', serverExecute:function(node) { if (node.metrics['M'] && node.metrics['M'].value == 'MOTION' && (Date.now() - new Date(node.metrics['M'].updated).getTime() < 2000)) { sendEmail('MOTION DETECTED', 'MOTION WAS DETECTED ON NODE: [' + node._id + ':' + node.label.replace(/\{.+\}/ig, '') + '] @ ' + new Date().toLocaleTimeString()); }; } },
12+
motionSMS : { label:'Motion : SMS', icon:'comment', descr:'Send SMS when MOTION is detected', serverExecute:function(node) { if (node.metrics['M'] && node.metrics['M'].value == 'MOTION' && (Date.now() - new Date(node.metrics['M'].updated).getTime() < 2000)) { sendSMS('MOTION DETECTED', 'MOTION WAS DETECTED ON NODE: [' + node._id + ':' + node.label.replace(/\{.+\}/ig, '') + '] @ ' + new Date().toLocaleTimeString()); }; } },
13+
14+
motionSMSLimiter : { label:'Motion : SMS Limited', icon:'comment', descr:'Send SMS when MOTION is detected, once per time limit (setting)',
15+
serverExecute:function(node) {
16+
if (node.metrics['M'] && node.metrics['M'].value == 'MOTION' && (Date.now() - node.metrics['M'].updated < 2000)) /*check if M metric exists and value is MOTION, received less than 2s ago*/
17+
{
18+
var approveSMS = false;
19+
20+
if (node.metrics['M'].lastSMS) /*check if lastSMS value is not NULL ... */
21+
{
22+
var repeatLimit = settings.general.smsRepeatLimit != undefined ? settings.general.smsRepeatLimit.value : 0;
23+
if (Date.now() - node.metrics['M'].lastSMS > repeatLimit) /*check if lastSMS timestamp is more than 1hr ago*/
24+
{
25+
approveSMS = true;
26+
}
27+
}
28+
else
29+
{
30+
approveSMS = true;
31+
}
32+
33+
if (approveSMS)
34+
{
35+
node.metrics['M'].lastSMS = Date.now();
36+
sendSMS('MOTION!', 'MOTION DETECTED ON NODE [' + node._id + ':' + node.label.replace(/\{.+\}/ig, '') + '] @ ' + new Date().toLocaleTimeString());
37+
db.update({ _id: node._id }, { $set : node}, {}, function (err, numReplaced) { console.log(` [${node._id}] DB-Updates:${numReplaced}`);}); /*save lastSMS timestamp to DB*/
38+
}
39+
else console.log(` [${node._id}] MOTION SMS skipped`);
40+
};
41+
}
42+
},
43+
44+
motionEmailSnapshot : { label:'Motion : Email+IPCam Snapshot', icon:'camera', descr:'Send email when MOTION is detected, with snapshot from IPCamera',
45+
serverExecute:function(node) {
46+
if (node.metrics['M'] && node.metrics['M'].value == 'MOTION' && (Date.now() - new Date(node.metrics['M'].updated).getTime() < 2000))
47+
{
48+
db.findOne({ _id : node._id }, function (err, dbNode) {
49+
var snapshotURL = dbNode.settings && dbNode.settings.ipcam_snapURL ? dbNode.settings.ipcam_snapURL : (metricsDef.motes[dbNode.type].settings.ipcam_snapURL || settings.misc.ipcam_snapURL.value);
50+
sendEmail('MOTION DETECTED', 'MOTION DETECTED ON NODE: [' + dbNode._id + ':' + dbNode.label.replace(/\{.+\}/ig, '') + '] @ ' + new Date().toLocaleTimeString(), [{path: snapshotURL, filename: 'snapshot.jpg'}]);
51+
});
52+
}
53+
}
54+
},
55+
56+
mailboxSMS : { label:'Mailbox open : SMS', icon:'comment', descr:'Send SMS when mailbox is opened', serverExecute:function(node) { if (node.metrics['M'] && node.metrics['M'].value == 'MOTION' && (Date.now() - new Date(node.metrics['M'].updated).getTime() < 2000)) { sendSMS('MAILBOX OPENED', 'Mailbox opened [' + node._id + ':' + node.label.replace(/\{.+\}/ig, '') + '] @ ' + new Date().toLocaleTimeString()); }; } },
57+
motionLightON23 : { label:'Motion: SM23 ON!', icon:'action', descr:'Turn SwitchMote:23 ON when MOTION is detected', serverExecute:function(node) { if (node.metrics['M'] && node.metrics['M'].value == 'MOTION' && (Date.now() - new Date(node.metrics['M'].updated).getTime() < 2000)) { sendMessageToNode({nodeId:23, action:'MOT:1'}); }; } },
58+
59+
}
60+
61+
exports.motes = {
62+
MotionMote: {
63+
label : 'Motion Sensor',
64+
icon : 'icon_motion.png',
65+
settings: { lowVoltageValue: '', ipcam_snapURL: '' }, //blank will make it inherit from global settings.json lowVoltageValue, a specific value overrides the general setting, user can always choose his own setting in the UI
66+
},
67+
Mailbox: {
68+
label : 'Mailbox',
69+
icon : 'icon_mailbox.png',
70+
settings: { lowVoltageValue: '', ipcam_snapURL: '' }, //blank will make it inherit from global settings.json lowVoltageValue, a specific value overrides the general setting, user can always choose his own setting in the UI
71+
},
72+
}

metrics/_LowPowerLab/sonarmote.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//SonarMote - https://lowpowerlab.com/shop/product/129
2+
3+
exports.metrics = {
4+
sonar : { name:'CM', regexp:/\b([\d\.]+)cm?\b/i, value:'', unit:'cm', pin:1, graph:1, graphOptions: { legendLbl:'Level', lines: { lineWidth:1 }, colors:['#09c']} },
5+
}
6+
7+
exports.events = {
8+
sumpSMS : { label:'SumpPump : SMS (below 20cm)', icon:'comment', descr:'Send SMS if water < 20cm below surface', serverExecute:function(node) { if (node.metrics['CM'] && node.metrics['CM'].value < 20 && (Date.now() - new Date(node.metrics['CM'].updated).getTime() < 2000)) { sendSMS('SUMP PUMP ALERT', 'Water is only 20cm below surface and rising - [' + node._id + '] ' + node.label.replace(/\{.+\}/ig, '') + ' @ ' + new Date().toLocaleTimeString()); }; } },
9+
}
10+
11+
exports.motes = {
12+
SonarMote: {
13+
label : 'Sonar Mote (Distance Sensor)',
14+
icon : 'icon_sonar.png',
15+
settings: { lowVoltageValue: '' }, //blank will make it inherit from global settings.json lowVoltageValue, a specific value overrides the general setting, user can always choose his own setting in the UI
16+
},
17+
}

metrics/_LowPowerLab/sprinklers.js

Lines changed: 42 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)