338 lines
10 KiB
JavaScript
338 lines
10 KiB
JavaScript
function setOutValveTo(controller_id, nextState) {
|
|
var state = Meteor.zoblak.server.controller_state(controller_id);
|
|
ControllerState.update(state._id, {
|
|
'$set': {
|
|
'state.out_valve': nextState,
|
|
'time': new Date(),
|
|
'set_by': 'server'
|
|
}
|
|
});
|
|
|
|
if (nextState === "open") {
|
|
ControllerState.update(state._id, {
|
|
'$set': {
|
|
'significantEvents.lastOutValveOpen': new Date(),
|
|
}
|
|
});
|
|
}
|
|
|
|
}
|
|
|
|
function setInValveTo(controller_id, nextState) {
|
|
var state = Meteor.zoblak.server.controller_state(controller_id);
|
|
ControllerState.update(state._id, {
|
|
'$set': {
|
|
'state.in_valve': nextState,
|
|
'time': new Date(),
|
|
'set_by': 'server'
|
|
}
|
|
});
|
|
|
|
if (nextState === "open") {
|
|
ControllerState.update(state._id, {
|
|
'$set': {
|
|
'significantEvents.lastInValveOpen': new Date(),
|
|
}
|
|
});
|
|
}
|
|
|
|
}
|
|
|
|
function requestNewPicture(controller_id) {
|
|
var state = Meteor.zoblak.server.controller_state(controller_id);
|
|
ControllerState.update(state._id, {
|
|
'$set': {
|
|
'state.picture_requested': 'true',
|
|
'time': new Date(),
|
|
'set_by': 'server'
|
|
}
|
|
});
|
|
};
|
|
|
|
function openInValve(controller_id) {
|
|
var state = Meteor.zoblak.server.controller_state(controller_id);
|
|
var config = state.config;
|
|
if (config.manualInflow) {
|
|
setInValveTo(controller_id, 'opening');
|
|
}
|
|
reactToSensorData(last_sensor_reading(controller_id));
|
|
}
|
|
|
|
function closeInValve(controller_id) {
|
|
var state = Meteor.zoblak.server.controller_state(controller_id);
|
|
var config = state.config;
|
|
if (config.manualInflow) {
|
|
setInValveTo(controller_id, 'closing');
|
|
}
|
|
reactToSensorData(last_sensor_reading(controller_id));
|
|
|
|
}
|
|
|
|
|
|
|
|
function openOutValve(controller_id) {
|
|
setOutValveTo(controller_id, 'opening');
|
|
setInValveTo(controller_id, 'closing');
|
|
var state = Meteor.zoblak.server.controller_state(controller_id);
|
|
var config = state.config;
|
|
var jobName = "Close out valve " + state.controller_id + " after draining";
|
|
console.log("Opening valve ", controller_id, jobName);
|
|
SyncedCron.remove(jobName);
|
|
SyncedCron.add({
|
|
name: jobName,
|
|
schedule: function(parser) {
|
|
var time = moment().add(config.draining_period_amount, config.draining_period_unit).toDate();
|
|
return parser.recur().on(time).fullDate();
|
|
},
|
|
job: function() {
|
|
closeOutValve(controller_id);
|
|
}
|
|
});
|
|
console.log(Meteor.sharedFunctions);
|
|
|
|
reactToSensorData(last_sensor_reading(controller_id));
|
|
}
|
|
|
|
function closeOutValve(controller_id) {
|
|
var state = Meteor.zoblak.server.controller_state(controller_id);
|
|
|
|
var jobName = "Close out valve " + state.controller_id + " after draining";
|
|
console.log("Closing valve ", controller_id, jobName);
|
|
SyncedCron.remove(jobName);
|
|
setOutValveTo(controller_id, 'closing');
|
|
console.log("Finished clearing cron ", controller_id);
|
|
|
|
console.log(Meteor.sharedFunctions);
|
|
|
|
reactToSensorData(last_sensor_reading(controller_id));
|
|
}
|
|
|
|
function clearLog() {
|
|
console.log("Removing sensor data");
|
|
SensorData.remove({});
|
|
}
|
|
|
|
function saveControllerConfig(controller_id, time, days, manualInflow) {
|
|
var state = Meteor.zoblak.server.controller_state(controller_id);
|
|
ControllerState.update(state._id, {
|
|
'$set': {
|
|
'config.automaticTimeOfDay': time,
|
|
'config.automaticDaysOfWeek': days,
|
|
'config.manualInflow': manualInflow
|
|
}
|
|
});
|
|
var jobName = "automatic_" + controller_id;
|
|
var times = time.split(":");
|
|
var hours = parseInt(times[0]);
|
|
var minutes = parseInt(times[1]);
|
|
|
|
SyncedCron.remove(jobName);
|
|
SyncedCron.add({
|
|
name: jobName,
|
|
schedule: function(parser) {
|
|
|
|
var period = parser.recur();
|
|
for (var i = 0; i < days.length; i++) {
|
|
period = period.and().on(parseInt(days[i])).dayOfWeek().on(hours).hour().on(minutes).minute();
|
|
}
|
|
return period;
|
|
},
|
|
job: function() {
|
|
openOutValve(controller_id);
|
|
}
|
|
});
|
|
}
|
|
|
|
function saveAlarmSettings(controller_id, minTemperature, maxTemperature, timeoutBox, timeoutPhone, smsNumbers) {
|
|
var state = Meteor.zoblak.server.controller_state(controller_id);
|
|
ControllerState.update(state._id, {
|
|
'$set': {
|
|
'config.minTemperature': parseFloat(minTemperature),
|
|
'config.maxTemperature': parseFloat(maxTemperature),
|
|
'config.timeoutBox': timeoutBox ? parseInt(timeoutBox) : null,
|
|
'config.timeoutPhone': timeoutPhone ? parseInt(timeoutPhone) : null,
|
|
'config.smsNumbers': smsNumbers,
|
|
'config.sms1': smsNumbers[0],
|
|
'config.sms2': smsNumbers[1],
|
|
'config.sms3': smsNumbers[2],
|
|
'config.sms4': smsNumbers[3]
|
|
}
|
|
});
|
|
var jobName = "automatic_alarm_" + controller_id;
|
|
|
|
SyncedCron.remove(jobName);
|
|
SyncedCron.add({
|
|
name: jobName,
|
|
schedule: function(parser) {
|
|
return parser.text('every 10 seconds');
|
|
},
|
|
job: function() {
|
|
reactToAlarmData(controller_id);
|
|
}
|
|
});
|
|
|
|
reactToAlarmData(controller_id);
|
|
}
|
|
|
|
|
|
// there are three states of alarm:
|
|
// 1. normal ( state.alarmTriggered: false, state.alarmStopped: null )
|
|
// 2. triggered ( state.alarmTriggered: true, state.alarmStopped: null )
|
|
// 3. silenced ( state.alarmTriggered: false, state.alarmStopped: (sometime) )
|
|
|
|
reactToAlarmData = function(controller_id) {
|
|
var reading = last_sensor_reading(controller_id);
|
|
var state = Meteor.zoblak.server.controller_state(controller_id);
|
|
var config = state.config;
|
|
|
|
var minTemperature = function(temperatures) {
|
|
// if it gets a lot colder than absolute zero
|
|
// we will have more problems than the bug in this code
|
|
if (temperatures.length <= 0) return -1000;
|
|
var minimal = parseFloat(temperatures[0]);
|
|
for (var i in temperatures) {
|
|
if (parseFloat(temperatures[i]) < minimal) {
|
|
minimal = parseFloat(temperatures[i]);
|
|
}
|
|
}
|
|
return minimal;
|
|
}
|
|
|
|
var maxTemperature = function(temperatures) {
|
|
// obviously - hell is not supported in this version
|
|
if (temperatures.length <= 0) return 1000;
|
|
var maximal = parseFloat(temperatures[0]);
|
|
for (var i in temperatures) {
|
|
if (parseFloat(temperatures[i]) > maximal) {
|
|
maximal = parseFloat(temperatures[i]);
|
|
}
|
|
}
|
|
return maximal;
|
|
}
|
|
|
|
var tooCold = config.minTemperature && (minTemperature(reading.temperatures) < config.minTemperature);
|
|
|
|
var tooHot = config.maxTemperature && (maxTemperature(reading.temperatures) > config.maxTemperature);
|
|
|
|
var minutesSinceLastBoxContact = reading.lastBoxContact ? moment(new Date()).diff(moment(reading.lastBoxContact), 'minutes') : -1;
|
|
var boxSilent = config.timeoutBox && minutesSinceLastBoxContact > config.timeoutBox;
|
|
|
|
var minutesSinceLastPhoneContact = state.lastPhoneContact ? moment(new Date()).diff(moment(state.lastPhoneContact), 'minutes') : -1;
|
|
var phoneSilent = false;//config.timeoutPhone && minutesSinceLastPhoneContact > config.timeoutPhone;
|
|
|
|
console.log("too ", tooCold, tooHot, boxSilent, phoneSilent);
|
|
console.log("lpc", state.lastPhoneContact);
|
|
console.log("mslpc", minutesSinceLastPhoneContact);
|
|
console.log("phoneSilent", phoneSilent);
|
|
|
|
|
|
if (tooCold || tooHot || boxSilent || phoneSilent) {
|
|
var alarmSilenced = !!state.state.alarmStopped;
|
|
if (!alarmSilenced) soundTheAlarm(controller_id, tooCold, tooHot, boxSilent, phoneSilent);
|
|
} else {
|
|
stopTheAlarm(controller_id, true);
|
|
}
|
|
}
|
|
|
|
function soundTheAlarm(controller_id, tooCold, tooHot, boxSilent, phoneSilent) {
|
|
var state = Meteor.zoblak.server.controller_state(controller_id);
|
|
var reason = {
|
|
tooHot: tooHot,
|
|
tooCold: tooCold,
|
|
boxSilent: boxSilent,
|
|
phoneSilent: phoneSilent
|
|
};
|
|
console.log("Alarmiram", reason);
|
|
|
|
var firstTime = {};
|
|
if (!state.state.alarmTriggered) {
|
|
firstTime = {
|
|
'state.alarmStarted': new Date()
|
|
}
|
|
};
|
|
|
|
var smsSent = !!state.state.alarmSmsSent;
|
|
var needsToSendSms = !smsSent // && phoneSilent;
|
|
|
|
var sendSmsPart = needsToSendSms ? { 'state.alarmSmsSent': true } : {};
|
|
|
|
ControllerState.update(state._id, {
|
|
'$set': Object.assign({
|
|
'state.alarmTriggered': true,
|
|
'state.alarmStopped': null,
|
|
'state.alarmReasons': reason
|
|
}, firstTime, sendSmsPart)
|
|
});
|
|
|
|
if (needsToSendSms) {
|
|
sendAlarmingSms(controller_id,reason, state.config.smsNumbers)
|
|
}
|
|
}
|
|
|
|
function sendAlarmingSms(controller_id,reason, numbers) {
|
|
for (var i in numbers) {
|
|
var number = numbers[i];
|
|
twilio = Twilio('AC10d7ed0bf54c1be4b1cd7133130e63f4', 'e133d3f02a69b79e93ad9ca1d73517d1');
|
|
twilio.sendSms({
|
|
to: number, // Any number Twilio can deliver to
|
|
from: '+447481345235', // A number you bought from Twilio and can use for outbound communication
|
|
body: 'Zoblak alarm! Pokrenite aplikaciju! HITNO! http://agrar.zoblak.com/alarm?controller_id=' + controller_id // body of the SMS message
|
|
}, function(err, responseData) { //this function is executed when a response is received from Twilio
|
|
if (!err) { // "err" is an error received during the request, if any
|
|
// "responseData" is a JavaScript object containing data received from Twilio.
|
|
// A sample response from sending an SMS message is here (click "JSON" to see how the data appears in JavaScript):
|
|
// http://www.twilio.com/docs/api/rest/sending-sms#example-1
|
|
console.log(responseData.from); // outputs "+14506667788"
|
|
console.log(responseData.body); // outputs "word to your mother."
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function stopTheAlarm(controller_id, everythingIsBackToNormal = false) {
|
|
// time of alarm stopped is reset so that scheduled job can raise the alarm
|
|
// again
|
|
var timeOfStopping = (everythingIsBackToNormal) ? null : new Date();
|
|
var state = Meteor.zoblak.server.controller_state(controller_id);
|
|
ControllerState.update(state._id, {
|
|
'$set': {
|
|
'state.alarmTriggered': false,
|
|
'state.alarmStopped': timeOfStopping,
|
|
'state.alarmSmsSent': false
|
|
}
|
|
});
|
|
}
|
|
|
|
function last_sensor_reading(controller_id) {
|
|
var result = null;
|
|
|
|
if (controller_id) {
|
|
result = SensorData.find({
|
|
controllerId: controller_id
|
|
}, {
|
|
sort: {
|
|
created_at: -1
|
|
},
|
|
limit: 1
|
|
});
|
|
}
|
|
if (result && result.count() > 0) {
|
|
return result.fetch()[0];
|
|
} else {
|
|
return {}
|
|
}
|
|
}
|
|
|
|
|
|
Meteor.methods({
|
|
openOutValve: openOutValve,
|
|
closeOutValve: closeOutValve,
|
|
openInValve: openInValve,
|
|
closeInValve: closeInValve,
|
|
clearLog: clearLog,
|
|
saveControllerConfig: saveControllerConfig,
|
|
requestNewPicture: requestNewPicture,
|
|
saveAlarmSettings: saveAlarmSettings,
|
|
stopTheAlarm: stopTheAlarm
|
|
});
|