Files
old-tfm/app/server/methods.js
2019-07-28 11:48:50 +02:00

367 lines
11 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, sensorsEnabled, sensorNames) {
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],
'config.sensorsEnabled': sensorsEnabled,
'config.sensorNames': sensorNames
}
});
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 temperatures = (reading.temperatures || []).filter(function(temperature, index) {
var is_on = (index in config.sensorsEnabled)?config.sensorsEnabled[index]: true;
return Meteor.zoblak.shared.valid_temperature(temperature) && is_on;
});
console.log("Konfiguracija: ", controller_id, config);
var tooCold = config.minTemperature && (minTemperature(temperatures) < config.minTemperature);
var tooHot = config.maxTemperature && (maxTemperature(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);
callTheUser(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: '+19282124174', // 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 callTheUser(controller_id, reason, numbers) {
for (var i in numbers) {
var number = numbers[i];
twilio = Twilio('AC10d7ed0bf54c1be4b1cd7133130e63f4', 'e133d3f02a69b79e93ad9ca1d73517d1');
twilio.makeCall({
to: number, // Any number Twilio can call
from: '+441143031932', // A number you bought from Twilio and can use for outbound communication
url: 'https://handler.twilio.com/twiml/EH9491c24474db07ec52b598baa5724f1e' // A URL that produces an XML document (TwiML) which contains instructions for the call
}, function(err, responseData) {
//executed when the call has been initiated.
console.log(err); // outputs "+14506667788"
});
}
}
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
});