25 Commits

Author SHA1 Message Date
Senad Uka
98983ea50d FIxed bug with inverted the tankFull bit! 2016-04-09 07:29:24 +02:00
Senad Uka
c947980844 FIxed graphs / inverted the tankFull bit! 2016-04-09 07:11:08 +02:00
974f18e067 Merge pull request #14 from senaduka/pre_production_fixes
Pre production fixes
2016-03-26 10:29:01 +01:00
Senad Uka
534497e373 Fixed filling logic 2016-03-26 07:32:53 +01:00
Senad Uka
26052a9afc Created graphs without d3 ;) 2016-03-26 07:28:43 +01:00
Senad Uka
e9e808a1bc Merge pull request #13 from senaduka/image_vise
- fixed names to match barrell*
2016-03-25 20:19:17 +01:00
4197d06819 - fixed names to match barrell*
- added more tag to images
2016-03-22 21:54:13 +01:00
Senad Uka
36d1adc626 Fixed a typo 2016-03-20 08:00:14 +01:00
Senad Uka
2317ad3e61 Some css modifications 2016-03-20 07:54:20 +01:00
b2e619c2c9 Merge pull request #12 from senaduka/ui_modifications
Ui is completely changed
2016-03-19 09:30:36 +01:00
Senad Uka
748dd19a87 Ui is completely changed 2016-03-19 09:23:59 +01:00
Senad Uka
21d364bf52 Merge pull request #11 from senaduka/improved_icon_handling_and_pump_management
Added 4 new images for opening and closing out valve while barrell is…
2016-03-19 04:33:23 +01:00
5cd8420bcf Added 4 new images for opening and closing out valve while barrell is either full or not full... also tried to add additional constraints for pump handling in means of that the pump should not be pumping if the out valve is open or opening 2016-03-18 22:26:41 +01:00
Senad Uka
24a1b81b92 Merge pull request #10 from senaduka/barrel_image_handling
Added proper barrel image handling based on input / sensor readings
2016-03-13 19:00:51 +01:00
829d6cf338 Added proper barrel image handling based on input / sensor readings 2016-03-09 00:35:26 +01:00
a8ac425832 Merge pull request #9 from senaduka/in_valve_support
In valve support
2016-03-06 21:07:56 +01:00
Senad Uka
d15bc0e5a6 problem with python syntax 2016-03-06 14:49:26 +01:00
Senad Uka
ee828c544e syntax error fix 2016-03-06 14:47:42 +01:00
Senad Uka
28e5f7f1f0 safe key getting from the response 2016-03-06 14:46:42 +01:00
Senad Uka
ed8267b6ab ignore existing config 2016-03-06 14:14:26 +01:00
Senad Uka
99fdc768fc config/init.py removed 2016-03-06 14:13:25 +01:00
Senad Uka
c47e1be364 Finished in valve support 2016-03-06 14:07:33 +01:00
Senad Uka
8d9e42c147 reformatted code 2016-03-06 13:40:53 +01:00
Senad Uka
279e0f8652 Added suport for in valve 2016-03-06 13:40:53 +01:00
2395cc6ddf Merge pull request #8 from senaduka/optimize_subscriptions
Optimized subscriptions
2016-03-06 12:05:48 +01:00
33 changed files with 487 additions and 156 deletions

3
.gitignore vendored
View File

@@ -1,3 +1,6 @@
# configuration
controller/config/__init__.py
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]

View File

@@ -24,3 +24,6 @@ selaias:meteor-simpleweather
u2622:persistent-session
percolate:synced-cron
rzymek:moment-locale-bs
peppelg:bootstrap-3-modal
fortawesome:fontawesome
mfpierre:chartist-js

View File

@@ -28,6 +28,8 @@ ejson@1.0.7
email@1.0.8
es5-shim@4.1.14
fastclick@1.0.7
fortawesome:fontawesome@4.5.0
fourseven:scss@3.4.1
geojson-utils@1.0.4
handlebars@1.0.4
hot-code-push@1.0.0
@@ -46,6 +48,7 @@ localstorage@1.0.5
logging@1.0.8
meteor@1.1.10
meteor-base@1.0.1
mfpierre:chartist-js@1.6.1
minifiers@1.1.7
minimongo@1.0.10
mobile-experience@1.0.1
@@ -58,6 +61,7 @@ npm-bcrypt@0.7.8_2
npm-mongo@1.4.39_1
observe-sequence@1.0.7
ordered-dict@1.0.4
peppelg:bootstrap-3-modal@1.0.4
percolate:synced-cron@1.3.0
promise@0.5.1
random@1.0.5

BIN
app/app.tar.gz Normal file

Binary file not shown.

View File

@@ -2,12 +2,20 @@
padding: 40px 15px;
text-align: center;
}
.controller_selection {
padding-top: 10px;
}
#bucket_image {
width: 60%;
@media all and (orientation: portrait) {
#bucket_image {
width: 90%;
cursor: pointer;
}
}
@media all and (orientation: landscape) {
#bucket_image {
width: 40%;
cursor: pointer;
}
}

View File

@@ -1,88 +1,103 @@
<template name="settings">
<div class="col-md-3 col-md-offset-4">
<h1>Automatsko zaljevanje: </h1>
<select name="time_of_day" id="time_of_day">
<option value="00:00" selected={{ timeSelected "00:00" }}>00:00</option>
<option value="00:30" selected={{ timeSelected "00:30" }}>00:30</option>
<option value="01:00" selected={{ timeSelected "01:00" }}>01:00</option>
<option value="01:30" selected={{ timeSelected "01:30" }}>01:30</option>
<option value="02:00" selected={{ timeSelected "02:00" }}>02:00</option>
<option value="02:30" selected={{ timeSelected "02:30" }}>02:30</option>
<option value="03:00" selected={{ timeSelected "03:00" }}>03:00</option>
<option value="03:30" selected={{ timeSelected "03:30" }}>03:30</option>
<option value="04:00" selected={{ timeSelected "04:00" }}>04:00</option>
<option value="04:30" selected={{ timeSelected "04:30" }}>04:30</option>
<option value="05:00" selected={{ timeSelected "05:00" }}>05:00</option>
<option value="05:30" selected={{ timeSelected "05:30" }}>05:30</option>
<option value="06:00" selected={{ timeSelected "06:00" }}>06:00</option>
<option value="06:30" selected={{ timeSelected "06:30" }}>06:30</option>
<option value="07:00" selected={{ timeSelected "07:00" }}>07:00</option>
<option value="07:30" selected={{ timeSelected "07:30" }}>07:30</option>
<option value="08:00" selected={{ timeSelected "08:00" }}>08:00</option>
<option value="08:30" selected={{ timeSelected "08:30" }}>08:30</option>
<option value="09:00" selected={{ timeSelected "09:00" }}>09:00</option>
<option value="09:30" selected={{ timeSelected "09:30" }}>09:30</option>
<option value="10:00" selected={{ timeSelected "10:00" }}>10:00</option>
<option value="10:30" selected={{ timeSelected "10:30" }}>10:30</option>
<option value="11:00" selected={{ timeSelected "11:00" }}>11:00</option>
<option value="11:30" selected={{ timeSelected "11:30" }}>11:30</option>
<option value="12:00" selected={{ timeSelected "12:00" }}>12:00</option>
<option value="12:30" selected={{ timeSelected "12:30" }}>12:30</option>
<option value="13:00" selected={{ timeSelected "13:00" }}>13:00</option>
<option value="13:30" selected={{ timeSelected "13:30" }}>13:30</option>
<option value="14:00" selected={{ timeSelected "14:00" }}>14:00</option>
<option value="14:30" selected={{ timeSelected "14:30" }}>14:30</option>
<option value="15:00" selected={{ timeSelected "15:00" }}>15:00</option>
<option value="15:30" selected={{ timeSelected "15:30" }}>15:30</option>
<option value="16:00" selected={{ timeSelected "16:00" }}>16:00</option>
<option value="16:30" selected={{ timeSelected "16:30" }}>16:30</option>
<option value="17:00" selected={{ timeSelected "17:00" }}>17:00</option>
<option value="17:30" selected={{ timeSelected "17:30" }}>17:30</option>
<option value="18:00" selected={{ timeSelected "18:00" }}>18:00</option>
<option value="18:30" selected={{ timeSelected "18:30" }}>18:30</option>
<option value="19:00" selected={{ timeSelected "19:00" }}>19:00</option>
<option value="19:30" selected={{ timeSelected "19:30" }}>19:30</option>
<option value="20:00" selected={{ timeSelected "20:00" }}>20:00</option>
<option value="20:30" selected={{ timeSelected "20:30" }}>20:30</option>
<option value="21:00" selected={{ timeSelected "21:00" }}>21:00</option>
<option value="21:30" selected={{ timeSelected "21:30" }}>21:30</option>
<option value="22:00" selected={{ timeSelected "22:00" }}>22:00</option>
<option value="22:30" selected={{ timeSelected "22:30" }}>22:30</option>
<option value="23:00" selected={{ timeSelected "23:00" }}>23:00</option>
<option value="23:30" selected={{ timeSelected "23:30" }}>23:30</option>
</select>
<div>
<div class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<input type="checkbox" id="day_1" name="day_1" value="2" class="day_checkbox" checked="{{ dayChecked '2' }}" />
<label for="day_1">Ponedjeljak</label>
</div>
<div>
<input type="checkbox" id="day_2" name="day_2" value="3" class="day_checkbox" checked="{{ dayChecked '3' }}" />
<label for="day_2">Utorak</label>
</div>
<div>
<input type="checkbox" id="day_3" name="day_3" value="4" class="day_checkbox" checked="{{ dayChecked '4' }}" />
<label for="day_3">Srijeda</label>
</div>
<div>
<input type="checkbox" id="day_4" name="day_4" value="5" class="day_checkbox" checked="{{ dayChecked '5' }}" />
<label for="day_4">Četvrtak</label>
</div>
<div>
<input type="checkbox" id="day_5" name="day_5" value="6" class="day_checkbox" checked="{{ dayChecked '6' }}" />
<label for="day_5">Petak</label>
</div>
<div>
<input type="checkbox" id="day_6" name="day_6" value="7" class="day_checkbox" checked="{{ dayChecked '7' }}" />
<label for="day_6">Subota</label>
</div>
<div>
<input type="checkbox" id="day_7" name="day_7" value="1" class="day_checkbox" checked="{{ dayChecked '1' }}" />
<label for="day_7">Nedjelja</label>
</div>
<div class="modal-header">
<h4 class="modal-title">Automatsko zaljevanje</h4>
</div>
<div>
<button id="save_settings" name="save_settings">Zapamti</button>
<div class="modal-body">
<div class="form-group">
<select name="time_of_day" id="time_of_day" class="form-control">
<option value="00:00" selected={{ timeSelected "00:00" }}>00:00</option>
<option value="00:30" selected={{ timeSelected "00:30" }}>00:30</option>
<option value="01:00" selected={{ timeSelected "01:00" }}>01:00</option>
<option value="01:30" selected={{ timeSelected "01:30" }}>01:30</option>
<option value="02:00" selected={{ timeSelected "02:00" }}>02:00</option>
<option value="02:30" selected={{ timeSelected "02:30" }}>02:30</option>
<option value="03:00" selected={{ timeSelected "03:00" }}>03:00</option>
<option value="03:30" selected={{ timeSelected "03:30" }}>03:30</option>
<option value="04:00" selected={{ timeSelected "04:00" }}>04:00</option>
<option value="04:30" selected={{ timeSelected "04:30" }}>04:30</option>
<option value="05:00" selected={{ timeSelected "05:00" }}>05:00</option>
<option value="05:30" selected={{ timeSelected "05:30" }}>05:30</option>
<option value="06:00" selected={{ timeSelected "06:00" }}>06:00</option>
<option value="06:30" selected={{ timeSelected "06:30" }}>06:30</option>
<option value="07:00" selected={{ timeSelected "07:00" }}>07:00</option>
<option value="07:30" selected={{ timeSelected "07:30" }}>07:30</option>
<option value="08:00" selected={{ timeSelected "08:00" }}>08:00</option>
<option value="08:30" selected={{ timeSelected "08:30" }}>08:30</option>
<option value="09:00" selected={{ timeSelected "09:00" }}>09:00</option>
<option value="09:30" selected={{ timeSelected "09:30" }}>09:30</option>
<option value="10:00" selected={{ timeSelected "10:00" }}>10:00</option>
<option value="10:30" selected={{ timeSelected "10:30" }}>10:30</option>
<option value="11:00" selected={{ timeSelected "11:00" }}>11:00</option>
<option value="11:30" selected={{ timeSelected "11:30" }}>11:30</option>
<option value="12:00" selected={{ timeSelected "12:00" }}>12:00</option>
<option value="12:30" selected={{ timeSelected "12:30" }}>12:30</option>
<option value="13:00" selected={{ timeSelected "13:00" }}>13:00</option>
<option value="13:30" selected={{ timeSelected "13:30" }}>13:30</option>
<option value="14:00" selected={{ timeSelected "14:00" }}>14:00</option>
<option value="14:30" selected={{ timeSelected "14:30" }}>14:30</option>
<option value="15:00" selected={{ timeSelected "15:00" }}>15:00</option>
<option value="15:30" selected={{ timeSelected "15:30" }}>15:30</option>
<option value="16:00" selected={{ timeSelected "16:00" }}>16:00</option>
<option value="16:30" selected={{ timeSelected "16:30" }}>16:30</option>
<option value="17:00" selected={{ timeSelected "17:00" }}>17:00</option>
<option value="17:30" selected={{ timeSelected "17:30" }}>17:30</option>
<option value="18:00" selected={{ timeSelected "18:00" }}>18:00</option>
<option value="18:30" selected={{ timeSelected "18:30" }}>18:30</option>
<option value="19:00" selected={{ timeSelected "19:00" }}>19:00</option>
<option value="19:30" selected={{ timeSelected "19:30" }}>19:30</option>
<option value="20:00" selected={{ timeSelected "20:00" }}>20:00</option>
<option value="20:30" selected={{ timeSelected "20:30" }}>20:30</option>
<option value="21:00" selected={{ timeSelected "21:00" }}>21:00</option>
<option value="21:30" selected={{ timeSelected "21:30" }}>21:30</option>
<option value="22:00" selected={{ timeSelected "22:00" }}>22:00</option>
<option value="22:30" selected={{ timeSelected "22:30" }}>22:30</option>
<option value="23:00" selected={{ timeSelected "23:00" }}>23:00</option>
<option value="23:30" selected={{ timeSelected "23:30" }}>23:30</option>
</select>
</div>
<div class="form-group">
<div>
<input type="checkbox" id="day_1" name="day_1" value="2" class="day_checkbox form_control" checked="{{ dayChecked '2' }}" />
<label for="day_1">Ponedjeljak</label>
</div>
<div>
<input type="checkbox" id="day_2" name="day_2" value="3" class="day_checkbox form_control" checked="{{ dayChecked '3' }}" />
<label for="day_2">Utorak</label>
</div>
<div>
<input type="checkbox" id="day_3" name="day_3" value="4" class="day_checkbox form_control" checked="{{ dayChecked '4' }}" />
<label for="day_3">Srijeda</label>
</div>
<div>
<input type="checkbox" id="day_4" name="day_4" value="5" class="day_checkbox form_control" checked="{{ dayChecked '5' }}" />
<label for="day_4">Četvrtak</label>
</div>
<div>
<input type="checkbox" id="day_5" name="day_5" value="6" class="day_checkbox form_control" checked="{{ dayChecked '6' }}" />
<label for="day_5">Petak</label>
</div>
<div>
<input type="checkbox" id="day_6" name="day_6" value="7" class="day_checkbox form_control" checked="{{ dayChecked '7' }}" />
<label for="day_6">Subota</label>
</div>
<div>
<input type="checkbox" id="day_7" name="day_7" value="1" class="day_checkbox form_control" checked="{{ dayChecked '1' }}" />
<label for="day_7">Nedjelja</label>
</div>
</div>
</div>
<div class="modal-footer">
<button id="save_settings" class="btn btn-default" name="save_settings" data-dismiss="modal">Zapamti</button>
</div>
</div>
</div>
</div>

View File

@@ -1,5 +1,5 @@
<template name="start">
<div class="col-lg-12 text-center">
<div class="col-lg-12 text-center form">
{{>state}}
</div>
</template>

View File

@@ -1,24 +1,18 @@
<template name="state">
<div class="col-md-3 col-md-offset-4">
<div class="col-md-12">
{{#with controller_state}}
<h1>{{controller_id}}</h1>
<img src="{{ bucket_image }}" class="img-responsive center-block" id="bucket_image" /> {{#with last_sensor_reading}}
<div>
<strong>{{ temperatureValue }} °C, {{ humidityValue }} % </strong>
<img src="{{ bucket_image }}" class="img-responsive center-block" id="bucket_image" />
<div class="text-center">
{{controller_id}}:
{{#with last_sensor_reading}} <strong> {{ temperatureValue }} °C, {{ humidityValue }} % </strong> {{/with}}
</div>
<div>
<div class="text-center">
Automatsko zalijevanje:<br /> <strong>{{ pretty_days config.automaticDaysOfWeek }} {{ pretty_time config.automaticDaysOfWeek config.automaticTimeOfDay }}</strong> <button id="run_settings" class="btn btn-default"> <i class="fa fa-wrench"></i> </button>
</div>
{{/with}}
<div>Otpusni ventil: {{pretty_valve state.out_valve }}</div>
<div>Zadnja komunikacija: {{ last_communication_time }}</div>
{{/with}}
<div>
<button id="water_now" class="{{ water_now_button_class }}">Zalij sada</button>
<button id="stop_water_now" class="{{ stop_button_class }}">Prekini zalijevanje</button>
</div>
</div>
<div class="col-md-1">
</div>
</template>

View File

@@ -1,7 +1,6 @@
function controller_state() {
var controllerId = Session.get('controller_id');
result = ControllerState.findOne({});
console.log("jupiii", result);
if (!result) {
result = {}
};
@@ -45,37 +44,60 @@ Template.state.helpers({
bucket_image: function() {
var sensor = last_sensor_reading();
if (sensor && sensor.tankFull === "1") {
return "/images/barell_full.png";
} else {
return "/images/barell_draining.png";
}
var stateObject = controller_state();
if (sensor) {
if (parseInt(sensor.tankFull) === 0 && stateObject.state.in_valve === 'open' && stateObject.state.out_valve === 'closed') return "/images/barrellFillingUp.png";
else if (parseInt(sensor.tankFull) === 1 && (stateObject.state.out_valve === 'closed')) return "/images/barrellFull.png";
else if (parseInt(sensor.tankFull) === 1 && (stateObject.state.out_valve === 'opening')) return "/images/barrellStartWateringFull.png";
else if (parseInt(sensor.tankFull) === 1 && (stateObject.state.out_valve === 'open')) return "/images/barrellWateringFull.png";
else if (parseInt(sensor.tankFull) === 1 && (stateObject.state.out_valve === 'closing')) return "/images/barrellStopWateringFull.png";
else if (parseInt(sensor.tankFull) === 0 && (stateObject.state.out_valve === 'closed')) return "/images/barrellNotFull.png";
else if (parseInt(sensor.tankFull) === 0 && (stateObject.state.out_valve === 'opening')) return "/images/barrellStartWateringNotFull.png";
else if (parseInt(sensor.tankFull) === 0 && (stateObject.state.out_valve === 'open')) return "/images/barrellWateringNotFull.png"
else if (parseInt(sensor.tankFull) === 0 && (stateObject.state.out_valve === 'closing')) return "/images/barrellStopWateringNotFull.png"
else return "/images/statusAmber.png";
} else return "/images/statusRed.png";
},
last_sensor_reading: last_sensor_reading,
last_communication_time: function() {
return moment(controller_state().time).fromNow();
},
water_now_button_class: function() {
var stateObject = controller_state();
if (stateObject.state && (stateObject.state.out_valve === 'open' || stateObject.state.out_valve === 'opening')) {
return 'hidden';
return 'hidden btn btn-success';
} else {
return '';
return 'btn btn-success';
}
},
stop_button_class: function() {
var stateObject = controller_state();
if (stateObject.state && (stateObject.state.out_valve === 'closed' || stateObject.state.out_valve === 'closing')) {
return 'hidden';
return 'hidden btn btn-success';
} else {
return '';
return 'btn btn-success';
}
},
pretty_days: function(daysInNumbers) {
var days = ["Nedjelja", "Ponedjeljak", "Utorak", "Srijeda", "Četvrtak", "Petak", "Subota"];
if (!daysInNumbers || daysInNumbers.length == 0) {
return "Nikad"
}
else if (daysInNumbers.length == 7) {
return "Svaki dan"
} else {
return daysInNumbers.map(function(number) {
return days[number -1 ];
}).join(", ");
}
},
pretty_time: function(daysInNumbers, time) {
if (!daysInNumbers || daysInNumbers.length == 0) {
return ""
} else {
return " u " + time;
}
}
});
Template.state.events({
@@ -87,6 +109,14 @@ Template.state.events({
'click #stop_water_now': function() {
var controller_id = Session.get('controller_id');
Meteor.call('closeOutValve', controller_id)
},
'click #run_settings': function() {
Modal.show('settings');
},
'click #bucket_image': function() {
Modal.show('state_details', controller_state());
}
});

View File

@@ -0,0 +1,44 @@
<template name="state_details">
<div class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Detalji</h4>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-12 chart">
<div><strong>Otpusni ventil:</strong> {{pretty_valve state.out_valve }}</div>
<div><strong>Ulazni ventil/pumpa:</strong> {{pretty_valve state.in_valve }}</div>
<div><strong>Zadnja komunikacija: {{ last_communication_time }}</strong>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12 chart">
<h3>Temperatura:</h3>
<div id="temperature_graph">
</div>
</div>
</div>
<div class="row">
<div class="col-md-12 chart">
<h3>Vlažnost:</h3>
<div id="humidity_graph">
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button id="close_details" class="btn btn-default" name="close_details" data-dismiss="modal">Zatvori</button>
</div>
</div>
</div>
</div>
</template>

153
app/client/state_details.js Normal file
View File

@@ -0,0 +1,153 @@
Template.state_details.helpers({
pretty_valve: function(state) {
if (state === 'open') return "Otvoren";
if (state === 'opening') return "Otvara se";
if (state === 'closing') return "Zatvara se";
if (state === 'closed') return "Zatvoren";
},
last_communication_time: function() {
return moment(this.time).fromNow();
}
});
Template.state_details.events({});
Template.state_details.rendered = function() {
this.node = this.find('#temperature_graph'); // our d3 code goes here
var yScale = d3.scale.linear()
.domain([0, 4])
.range([10, 60]);
var xScale = d3.scale.linear()
.domain([0, self.duration])
.range([0, $(self.timelineWrapper).width()])
};
function sensor_data_collection() {
var controllerId = Session.get('controller_id');
return SensorData.find({
controllerId: controllerId
}, {
sort: {
created_at: 1
}
});
}
Template.state_details.rendered = function() {
var self = this;
/*
* add point when new temperature is added
*/
setTimeout(function() {
self.autorun(function() {
sensor_data_collection().observe({
added: function(reading) {
buildTemperatureGraph();
buildHumidityGraph()
}
});
});
}, 1000);
}
/*
* Function to draw the graph
*/
function buildTemperatureGraph() {
var unfilteredReadings = sensor_data_collection();
// we want to show only 11 points from all data
var breakingPoint = Math.floor(countValues(unfilteredReadings) / 10);
var sensorReadings = filterDataPoints(unfilteredReadings, breakingPoint);
var times = sensorReadings.map(function(reading) {
return moment(reading.created_at).format("HH:mm:ss");
});
var values = sensorReadings.map(function(reading) {
return reading.temperatureValue;
});
var breakingPoint = Math.floor(times.length / 11);
return new Chartist.Line('#temperature_graph', {
labels: times,
series: [values]
}, {
fullWidth: true,
chartPadding: {
right: 40
}
});
}
/*
* Function to draw the graph
*/
function buildHumidityGraph() {
var unfilteredReadings = sensor_data_collection();
// we want to show only 11 points from all data - filtering will add
// the last one so 10 + 1 = 11
var breakingPoint = Math.floor(countValues(unfilteredReadings) / 10);
var sensorReadings = filterDataPoints(unfilteredReadings, breakingPoint);
var times = sensorReadings.map(function(reading) {
return moment(reading.created_at).format("HH:mm:ss");
});
var values = sensorReadings.map(function(reading) {
return reading.humidityValue;
});
return new Chartist.Line('#humidity_graph', {
labels: times,
series: [values]
}, {
fullWidth: true,
chartPadding: {
right: 40
}
});
}
function filterDataPoints(data, breakingPoint) {
if (breakingPoint === 0) {
return data;
}
var result = [];
var index = 0;
var lastUnpushedRow = null;
data.forEach(function(row) {
if (index % breakingPoint === 0) {
result.push(row);
lastUnpushedRow = null;
} else {
lastUnpushedRow = row;
}
index++;
});
// in order to always have the latest value
if(lastUnpushedRow) {
result.push(lastUnpushedRow);
}
return result;
}
// dirty hack for the complicated way of getting
// acual number of values
function countValues(data) {
var count = 0;
data.forEach(function(row) {
count++;
});
return count;
};

View File

@@ -4,7 +4,6 @@
<li role="presentation" class="{{ class_for 'start' }}"><a href="#">Stanje</a></li>
<li role="presentation" class="{{ class_for 'weather' }}"><a href="#">Vrijeme</a></li>
<li role="presentation" class="{{ class_for 'log' }}"><a href="#">Novosti</a></li>
<li role="presentation" class="{{ class_for 'settings' }}"><a href="#">Podešavanje</a></li>
<li role="presentation" class="controller_selection"> <input type="number" id="controller" name="controller" value="{{ selected_controller }}" min="1" max="99999"> <button id="switch" name="switch">Prebaci</button>
</li>
</ul>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@@ -9,7 +9,7 @@ Api.addRoute('sensorData', {
authRequired: false
}, {
post: function() {
console.log("Body params", this.bodyParams);
reactToSensorData(this.bodyParams);
SensorData.insert({
temperatureValue: parseFloat(this.bodyParams.temperatureValue),
humidityValue: parseFloat(this.bodyParams.humidityValue),
@@ -23,16 +23,48 @@ Api.addRoute('sensorData', {
});
function reactToSensorData(nextSensorReading) {
var controllerId = nextSensorReading.controllerId;
var state = stateOrDefault(controllerId).state;
console.log(nextSensorReading , state);
var shouldStartPumping = (!state.in_valve || state.in_valve === 'closed' || state.in_valve === 'closing') && ((parseInt(nextSensorReading.tankFull) === 0) && (state.out_valve === 'closed' || state.out_valve === 'closing'));
var shouldStopPumping = (state.in_valve === 'open' || state.in_valve === 'opening') && (parseInt(nextSensorReading.tankFull) === 1 || state.out_valve === 'open' || state.out_valve === 'opening');
if (shouldStartPumping) {
ControllerState.update({
controller_id: controllerId
}, {
'$set': {
'state.in_valve': 'opening',
'time': new Date(),
'set_by': 'server'
}
});
} else if (shouldStopPumping) {
ControllerState.update({
controller_id: controllerId
}, {
'$set': {
'state.in_valve': 'closing',
'time': new Date(),
'set_by': 'server'
}
});
}
}
Api.addRoute('state/:id', {
authRequired: false
}, {
post: function() {
console.log("Body params", this.bodyParams);
console.log("setting state", this.bodyParams);
return ControllerState.update({
controller_id: this.urlParams.id
}, {
'$set': {
'state.out_valve': this.bodyParams.out_valve,
'state.in_valve': this.bodyParams.in_valve,
'time': new Date(),
'set_by': 'client'
}
@@ -43,6 +75,7 @@ Api.addRoute('state/:id', {
}
});
function stateOrDefault(id) {
var stateEntry = ControllerState.findOne({
controller_id: id,
@@ -52,13 +85,13 @@ function stateOrDefault(id) {
stateEntry = ControllerState.insert({
controller_id: id,
state: {
out_valve: 'closed'
out_valve: 'closed',
in_valve: 'closed'
},
time: new Date(),
config: {
draining_period_amount: 5,
draining_period_amount: 60,
draining_period_unit: 'minutes'
},
set_by: 'server'
});
@@ -68,3 +101,6 @@ function stateOrDefault(id) {
});
return stateEntry;
}
Meteor.sharedFunctions = Meteor.sharedFunctions || {};
Meteor.sharedFunctions.reactToSensorData = reactToSensorData;

View File

@@ -40,7 +40,9 @@ function openOutValve(controller_id) {
closeOutValve(controller_id);
}
});
console.log("Finished adding cron ", controller_id);
console.log(Meteor.sharedFunctions);
Meteor.sharedFunctions.reactToSensorData(last_sensor_reading(controller_id));
}
function closeOutValve(controller_id) {
@@ -51,6 +53,10 @@ function closeOutValve(controller_id) {
SyncedCron.remove(jobName);
setOutValveTo(controller_id, 'closing');
console.log("Finished clearing cron ", controller_id);
console.log(Meteor.sharedFunctions);
Meteor.sharedFunctions.reactToSensorData(last_sensor_reading(controller_id));
}
function clearLog() {
@@ -88,6 +94,28 @@ function saveControllerConfig(controller_id, time, days) {
});
}
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,

View File

@@ -4,27 +4,10 @@ if (Meteor.isServer) {
SyncedCron.start();
});
// Global API configuration
var Api = new Restivus({
useDefaultAuth: true,
prettyJson: true
});
Api.addRoute('sensorData', {
authRequired: false
}, {
post: function() {
SensorData.insert({
temperatureValue: parseFloat(this.bodyParams.temperatureValue),
humidityValue: parseFloat(this.bodyParams.humidityValue),
tankFull: this.bodyParams.tankFull,
owner: this.bodyParams.owner,
created_at: new Date()
});
return [];
}
});
}

View File

@@ -3,7 +3,8 @@
GPIO_PIN_DHT = 4 # BCM
SENSORDATA_URL = 'http://tfm.meteor.com/api/v1.0/sensorData'
GPIO_PIN_TANKFULL = 20 # BCM
GPIO_PIN_VALVE = 21 # BCM
GPIO_PIN_OUT_VALVE = 21 # BCM
GPIO_PIN_IN_VALVE = 18 # BCM
API_BASE_URL = 'http://tfm.meteor.com/api/v1.0'
CONTROLLER_ID = '120' # every controller must have a different one
STATE_FILE = '/var/run/controller_state'

View File

@@ -8,7 +8,10 @@ import RPi.GPIO as GPIO
# Try to read the state of GPIO_PIN_TANKFULL
GPIO.setmode(GPIO.BCM)
GPIO.setup(config.GPIO_PIN_TANKFULL, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
tankFull = GPIO.input(config.GPIO_PIN_TANKFULL)
# tank sensor has inverse logic - 0 when it is full 1 when it is not
# so we are inverting its value here
tankFull = (not GPIO.input(config.GPIO_PIN_TANKFULL))
GPIO.cleanup()
print 'Bacva puna: {}'.format(tankFull)

View File

@@ -7,29 +7,56 @@ class Changer(object):
self.local_state = local_state
self.remote_state = remote_state
GPIO.setmode(GPIO.BCM) # Broadcom pin-numbering scheme
GPIO.setup(config.GPIO_PIN_VALVE, GPIO.OUT)
self.states = {
'opening': self.open_valve,
'closing': self.close_valve,
'open': self.open_valve,
'closed': self.close_valve
GPIO.setup(config.GPIO_PIN_OUT_VALVE, GPIO.OUT)
GPIO.setup(config.GPIO_PIN_IN_VALVE, GPIO.OUT)
self.out_valve_states = {
'opening': self.open_out_valve,
'closing': self.close_out_valve,
'open': self.open_out_valve,
'closed': self.close_out_valve
}
self.in_valve_states = {
'opening': self.open_in_valve,
'closing': self.close_in_valve,
'open': self.open_in_valve,
'closed': self.close_in_valve
}
def safe_remote_state(self, key):
if key in ['out_valve', 'in_valve']:
return self.remote_state.get(key, 'closed')
else:
return self.remote_state.get(key,'');
def process_change(self):
self.validate_states()
change = self.states.get(self.remote_state['out_valve'], None )
if change is not None:
change()
out_valve_change = self.out_valve_states.get(self.safe_remote_state('out_valve'), None )
if out_valve_change is not None:
out_valve_change()
in_valve_change = self.in_valve_states.get(self.safe_remote_state('in_valve'), None )
if in_valve_change is not None:
in_valve_change()
return self.local_state
def open_valve(self):
GPIO.output(config.GPIO_PIN_VALVE, GPIO.HIGH)
def open_in_valve(self):
GPIO.output(config.GPIO_PIN_IN_VALVE, GPIO.HIGH)
self.local_state['in_valve'] = 'open'
def close_in_valve(self):
GPIO.output(config.GPIO_PIN_IN_VALVE, GPIO.LOW)
self.local_state['in_valve'] = 'closed'
def open_out_valve(self):
GPIO.output(config.GPIO_PIN_OUT_VALVE, GPIO.HIGH)
self.local_state['out_valve'] = 'open'
def close_valve(self):
GPIO.output(config.GPIO_PIN_VALVE, GPIO.LOW)
def close_out_valve(self):
GPIO.output(config.GPIO_PIN_OUT_VALVE, GPIO.LOW)
self.local_state['out_valve'] = 'closed'
def validate_states(self):