29 Commits

Author SHA1 Message Date
Senad Uka
d2db89ffc0 Disabled temperature graph 2016-05-17 07:05:54 +02:00
Senad Uka
ac76654e2a Added last valve open time 2016-05-08 05:17:12 +02:00
2772baafbc Merge pull request #18 from senaduka/add_low_temperature
Lower temperature forecast
2016-05-02 18:13:22 +02:00
Senad Uka
3e0733bf3d Lower temperature forecast 2016-05-02 18:10:54 +02:00
Senad Uka
d9fa0bf2f5 Calculator done 2016-05-02 15:18:02 +02:00
Senad Uka
abff71ac06 Calculator done 2016-05-02 14:09:09 +02:00
Senad Uka
5a4fdbbd00 reversed logic 2016-04-10 12:11:54 +02:00
Senad Uka
96c67a6d00 gitignore 2016-04-10 11:58:18 +02:00
Senad Uka
3b8fb3dce4 fixed bug with rpio 2016-04-10 11:57:13 +02:00
0b4a886de6 Merge pull request #16 from senaduka/add_weather_again
weather works
2016-04-10 10:56:23 +02:00
Senad Uka
79df9d05b4 weather works 2016-04-10 10:47:11 +02:00
4df30c3523 Merge pull request #15 from senaduka/graph_fixing
FIxed graphs / inverted the tankFull bit!
2016-04-09 09:48:02 +02:00
Senad Uka
f886530f5f upgraded mongo and packages 2016-04-09 07:51:11 +02:00
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
37 changed files with 710 additions and 247 deletions

3
.gitignore vendored
View File

@@ -58,3 +58,6 @@ docs/_build/
# PyBuilder
target/
# meteor
app.tar.gz

View File

@@ -10,3 +10,4 @@ notices-for-facebook-graph-api-2
1.2.0-meteor-platform-split
1.2.0-cordova-changes
1.2.0-breaking-changes
1.3.0-split-minifiers-package

View File

@@ -12,7 +12,6 @@ session # Client-side reactive dictionary for your app
jquery # Helpful client-side library
tracker # Meteor's client-side reactive programming library
standard-minifiers # JS/CSS minifiers run for production mode
es5-shim # ECMAScript 5 compatibility for older browsers.
ecmascript # Enable ECMAScript2015+ syntax in app code
@@ -24,3 +23,8 @@ selaias:meteor-simpleweather
u2622:persistent-session
percolate:synced-cron
rzymek:moment-locale-bs
peppelg:bootstrap-3-modal
fortawesome:fontawesome
mfpierre:chartist-js
standard-minifier-css
standard-minifier-js

View File

@@ -1 +1 @@
METEOR@1.2.1
METEOR@1.3.1

View File

@@ -1,88 +1,95 @@
accounts-base@1.2.2
accounts-password@1.1.4
accounts-base@1.2.5
accounts-password@1.1.7
allow-deny@1.0.3
amplify@1.0.0
autoupdate@1.2.4
babel-compiler@5.8.24_1
babel-runtime@0.1.4
base64@1.0.4
binary-heap@1.0.4
blaze@2.1.3
blaze-html-templates@1.0.1
blaze-tools@1.0.4
boilerplate-generator@1.0.4
caching-compiler@1.0.0
caching-html-compiler@1.0.2
callback-hook@1.0.4
check@1.1.0
coffeescript@1.0.11
ddp@1.2.2
ddp-client@1.2.1
ddp-common@1.2.2
ddp-rate-limiter@1.0.0
ddp-server@1.2.2
deps@1.0.9
diff-sequence@1.0.1
ecmascript@0.1.6
ecmascript-runtime@0.2.6
ejson@1.0.7
email@1.0.8
es5-shim@4.1.14
fastclick@1.0.7
geojson-utils@1.0.4
handlebars@1.0.4
hot-code-push@1.0.0
html-tools@1.0.5
htmljs@1.0.5
http@1.1.1
huttonr:bootstrap3@3.3.6_6
huttonr:bootstrap3-assets@3.3.6_2
id-map@1.0.4
jquery@1.11.4
json@1.0.3
launch-screen@1.0.4
less@2.5.1
livedata@1.0.15
localstorage@1.0.5
logging@1.0.8
meteor@1.1.10
meteor-base@1.0.1
minifiers@1.1.7
minimongo@1.0.10
mobile-experience@1.0.1
mobile-status-bar@1.0.6
momentjs:moment@2.11.2
mongo@1.1.3
mongo-id@1.0.1
nimble:restivus@0.8.7
autoupdate@1.2.7
babel-compiler@6.6.1
babel-runtime@0.1.7
base64@1.0.7
binary-heap@1.0.7
blaze@2.1.6
blaze-html-templates@1.0.3
blaze-tools@1.0.7
boilerplate-generator@1.0.7
caching-compiler@1.0.3
caching-html-compiler@1.0.5
callback-hook@1.0.7
check@1.1.3
coffeescript@1.0.16
ddp@1.2.4
ddp-client@1.2.4
ddp-common@1.2.4
ddp-rate-limiter@1.0.3
ddp-server@1.2.5
deps@1.0.11
diff-sequence@1.0.4
ecmascript@0.4.2
ecmascript-runtime@0.2.9
ejson@1.0.10
email@1.0.11
es5-shim@4.5.9
fastclick@1.0.10
fortawesome:fontawesome@4.5.0
fourseven:scss@3.4.1
geojson-utils@1.0.7
hot-code-push@1.0.3
html-tools@1.0.8
htmljs@1.0.8
http@1.1.4
huttonr:bootstrap3@3.3.6_10
huttonr:bootstrap3-assets@3.3.6_3
id-map@1.0.6
jquery@1.11.7
launch-screen@1.0.10
less@2.5.7
livedata@1.0.17
localstorage@1.0.8
logging@1.0.11
meteor@1.1.13
meteor-base@1.0.3
mfpierre:chartist-js@1.6.1
minifier-css@1.1.10
minifier-js@1.1.10
minimongo@1.0.13
mobile-experience@1.0.3
mobile-status-bar@1.0.11
modules@0.5.2
modules-runtime@0.6.2
momentjs:moment@2.12.0
mongo@1.1.6
mongo-id@1.0.3
nimble:restivus@0.8.10
npm-bcrypt@0.7.8_2
npm-mongo@1.4.39_1
observe-sequence@1.0.7
ordered-dict@1.0.4
percolate:synced-cron@1.3.0
promise@0.5.1
random@1.0.5
rate-limit@1.0.0
reactive-dict@1.1.3
reactive-var@1.0.6
reload@1.1.4
retry@1.0.4
routepolicy@1.0.6
npm-mongo@1.4.42
observe-sequence@1.0.10
ordered-dict@1.0.6
peppelg:bootstrap-3-modal@1.0.4
percolate:synced-cron@1.3.2
promise@0.6.6
random@1.0.8
rate-limit@1.0.3
reactive-dict@1.1.6
reactive-var@1.0.8
reload@1.1.7
retry@1.0.6
routepolicy@1.0.9
rzymek:moment-locale-bs@2.9.0
selaias:meteor-simpleweather@0.6.8
service-configuration@1.0.5
session@1.1.1
sha@1.0.4
simple:json-routes@2.0.1
spacebars@1.0.7
spacebars-compiler@1.0.7
srp@1.0.4
standard-minifiers@1.0.2
templating@1.1.5
templating-tools@1.0.0
tracker@1.0.9
selaias:meteor-simpleweather@0.7.0
service-configuration@1.0.8
session@1.1.4
sha@1.0.6
simple:json-routes@2.1.0
spacebars@1.0.10
spacebars-compiler@1.0.10
srp@1.0.7
standard-minifier-css@1.0.5
standard-minifier-js@1.0.5
templating@1.1.8
templating-tools@1.0.3
tracker@1.0.12
u2622:persistent-session@0.4.4
ui@1.0.8
underscore@1.0.4
url@1.0.5
webapp@1.2.3
webapp-hashing@1.0.5
ui@1.0.10
underscore@1.0.7
url@1.0.8
webapp@1.2.7
webapp-hashing@1.0.8

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,17 +0,0 @@
var options = {
location: 40.7127+','+ 74.0059, // New York
unit: 'c',
success: function(weather) {
html = '<h2><i class="sw icon-'+weather.code+'"></i> '
html += weather.temp+'&deg;'+weather.units.temp+'</h2>';
html += '<ul><li>'+weather.city+', '+weather.region +'</li>';
html += '<li class="currently">'+weather.currently+'</li>';
$("#weather").html(html);
},
error: function(error) {
$("#weather").html('<p>'+error+'</p>');
}
}
Weather.options = options

View File

@@ -1,25 +1,19 @@
<template name="state">
<div class="col-md-3 col-md-offset-4">
<div>&nbsp;</div>
<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>Ulazni ventil/pumpa: {{pretty_valve state.in_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

@@ -44,39 +44,64 @@ 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({
'click #water_now': function() {
var controller_id = Session.get('controller_id');
@@ -86,6 +111,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,48 @@
<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><strong>Zadnje zaljevanje: {{ last_out_valve_open }}</strong>
</div>
<div><strong>Zadnje punjenje: {{ last_in_valve_open }}</strong>
</div>
</div>
</div>
<div class="row hidden">
<div class="col-md-12 chart">
<h3>Temperatura:</h3>
<div id="temperature_graph">
</div>
</div>
</div>
<div class="row hidden">
<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>

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

@@ -0,0 +1,159 @@
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();
},
last_out_valve_open: function() {
return moment(this.significantEvents.lastOutValveOpen).fromNow();
},
last_in_valve_open: function() {
return moment(this.significantEvents.lastInValveOpen).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>

9
app/client/weather.html Normal file
View File

@@ -0,0 +1,9 @@
<template name="weather">
<div>&nbsp;</div>
<div class="col-md-12">
<div class="text-center">
{{>simpleWeather}}
</div>
</div>
</template>

38
app/client/weather.js Normal file
View File

@@ -0,0 +1,38 @@
var options = {
location: 44.0123 + ',' + 18.19455, // New Visoko
unit: 'c',
success: function(weather) {
html = "<h2><i class='sw icon-" + weather.code + "'></i>";
html += weather.temp + '&deg;' + weather.units.temp + '</h2>';
html += '<ul><li>' + weather.city + ', ' + weather.region + '</li>';
html += "<li class='currently'>" + weather.currently + '</li>';
html += '<hr />';
for (var i = 0; i < 5; i++) {
var forecast = weather.forecast[i];
html += '<div>';
html += "<h2><i class='sw icon-" + forecast.code + "'></i>";
html += forecast.high + '&deg;' + weather.units.temp + '</h2>';
html += "<li class='currently'> Najniža dnevna: "+ forecast.low + ' &deg; ' + weather.units.temp + "<br />" + daysInBosnian[forecast.day] + ': ' + forecast.text + '</li>';
html += "</div>";
}
$('#weather').html(html);
},
error: function(error) {
$('#weather').html('<p>' + error + '</p>');
}
}
var daysInBosnian = {
'Sun': 'Nedjelja',
'Mon': 'Ponedjeljak',
'Tue': 'Utorak',
'Wed': 'Srijeda',
'Thu': 'Četvrtak',
'Fri': 'Petak',
'Sat': 'Subota'
};
Weather.options = options

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

@@ -24,10 +24,12 @@ Api.addRoute('sensorData', {
function reactToSensorData(nextSensorReading) {
console.log("reacting to sensor");
var controllerId = nextSensorReading.controllerId;
var state = stateOrDefault(controllerId).state;
var shouldStartPumping = (parseInt(nextSensorReading.tankFull) === 0 && (!state.in_valve || state.in_valve === 'closed'));
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({
@@ -35,14 +37,12 @@ function reactToSensorData(nextSensorReading) {
}, {
'$set': {
'state.in_valve': 'opening',
'significantEvents.lastInValveOpen': new Date(),
'time': new Date(),
'set_by': 'server'
}
});
}
var shouldStopPumping = parseInt(nextSensorReading.tankFull) === 1 && (state.in_valve === 'open' || state.in_valve === 'opening');
if (shouldStopPumping) {
} else if (shouldStopPumping) {
ControllerState.update({
controller_id: controllerId
}, {
@@ -91,7 +91,7 @@ function stateOrDefault(id) {
},
time: new Date(),
config: {
draining_period_amount: 5,
draining_period_amount: 60,
draining_period_unit: 'minutes'
},
set_by: 'server'
@@ -102,3 +102,6 @@ function stateOrDefault(id) {
});
return stateEntry;
}
Meteor.sharedFunctions = Meteor.sharedFunctions || {};
Meteor.sharedFunctions.reactToSensorData = reactToSensorData;

View File

@@ -21,6 +21,15 @@ function setOutValveTo(controller_id, nextState) {
'set_by': 'server'
}
});
if(nextState === "open") {
ControllerState.update(state._id, {
'$set': {
'significantEvents.lastOutValveOpen': new Date(),
}
});
}
}
function openOutValve(controller_id) {
@@ -40,7 +49,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 +62,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 +103,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

@@ -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 = (GPIO.input(config.GPIO_PIN_TANKFULL) == GPIO.LOW)
GPIO.cleanup()
print 'Bacva puna: {}'.format(tankFull)
@@ -29,7 +32,7 @@ humidity, temperature = Adafruit_DHT.read_retry(SENSOR_TYPE, config.GPIO_PIN_DHT
# guarantee the timing of calls to read the sensor).
# If this happens try again!
if temperature is not None and humidity is not None:
response = requests.post(config.SENSORDATA_URL, json={"owner": owner, "temperatureValue": temperature, "humidityValue":humidity, "tankFull":tankFull,
response = requests.post(config.SENSORDATA_URL, json={"owner": owner, "temperatureValue": temperature, "humidityValue":humidity, "tankFull": "1" if tankFull else "0",
"controllerId": controller_id
})
print 'Temp={0:0.1f}*C'.format(temperature)

136
misc/calculator.html Normal file
View File

@@ -0,0 +1,136 @@
<html>
<head>
<meta charset="UTF-8">
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-2.2.3.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>
</head>
<body>
<div class="jumbotron">
<div class="lead text-center">
<img src="./zoblak.png" width="30%" />
<br /> Zoblak Agrar Plus se isplati za <span id="vrijemeZaBreakEven" class="bg-success"></span> mjeseci. U periodu od <span id="brojSezona"></span> godine ostvaruje uštedu u novcu od <span id="iznosUstede" class="bg-success"></span> KM, te uštedu u vremenu od <span id="satiUProjekciji" class="bg-success"></span> sati za podešeni slučaj.
</div>
</div>
<div class="container-fluid">
<div class="row">
<div class="col-md-6">
<p>Na troškove goriva mjesečno trošite <span id="iznosGoriva"></span> KM. Na rad potreban za zalijevanje trošite mjesečno <span id="satiMjesecno"></span> sati što uz zadanu satnicu iznosi <span id="plata"></span> KM. Zajedno to je <span id="ukupnoTroskovaMjesecno"></span> KM troška na zalijevanje. </p>
<p>Okvirna cijena Zoblak Agrar Plus iznosi <span id="bazaCijeneZoblaka"></span> KM + sistem navodnjavanja</p>
<p class="bg-primary">NAPOMENA: finalna cijena može biti niža ili viša i poznata je nakon izrade projekta).</p>
</div>
<div class="col-md-6 col-sm-6">
<form method="post">
<div class="form-group ">
<label class="control-label " for="number">
Vaša udaljenost od polja u km
</label>
<input class="form-control ulazni-parametar" id="pudaljenost" type="number" min="1" max="200" step="0.1" value="5" />
</div>
<div class="form-group ">
<label class="control-label " for="number1">
Prosjecna potro&scaron;nja automobila na 100 km
</label>
<input class="form-control ulazni-parametar" id="ppotrosnja" type="number" min="2" max="15" value="7" step="0.1" />
</div>
<div class="form-group ">
<label class="control-label " for="select">
Koliko često treba zaljevati kulturu?
</label>
<select class="select form-control" id="pfrekvencija_zalijevanja">
<option value="1">
Svaki dan
</option>
<option value="2">
Jednom u dva dana
</option>
<option value="3">
Jednom u tri dana
</option>
<option value="4">
Jednom u četiri dana
</option>
</select>
</div>
<div class="form-group ">
<label class="control-label " for="number2">
Koliko vremena traje put prema polju (u oba smijera) i jedan ciklus zalijevanja? U satima.
</label>
<input class="form-control ulazni-parametar" id="pvrijeme" type="number" min="2" max="10" value="1.5" step="0.5" />
</div>
<div class="form-group ">
<label class="control-label " for="number3">
Satnica osobe koja zalijeva u KM
</label>
<input class="form-control ulazni-parametar" id="psatnica" type="number" min="1" max="100" value="20" step="1" />
</div>
<div class="form-group ">
<label class="control-label " for="number3">
Trajanje sezone u mjesecima
</label>
<input class="form-control ulazni-parametar" id="psezona" type="number" min="1" max="12" value="4" step="1" />
</div>
<div class="form-group ">
<label class="control-label " for="number3">
Na koliko godina/sezona se kalkulacija vrši
</label>
<input class="form-control ulazni-parametar" id="pbroj_sezona" type="number" min="1" max="10" value="3" step="1" />
</div>
</form>
</div>
</div>
</div>
<script type="text/javascript">
function iskalkulisi() {
var udaljenost = parseFloat($('#pudaljenost').val()) || 10;
var potrosnja = parseFloat($('#ppotrosnja').val()) || 7;
var frekvencija = parseInt($('#pfrekvencija_zalijevanja').val()) || 2;
var vrijeme = parseFloat($('#pvrijeme').val()) || 1.5;
var satnica = parseFloat($('#psatnica').val()) || 20;
var sezona = parseInt($('#psezona').val()) || 4;
var brojSezona = parseInt($('#pbroj_sezona').val()) || 3;
var bazaCijeneZoblaka = 1000;
var bazaCijeneVentila = 0;
var bazaCijeneGoriva = 1.70;
var godisnjeOdrzavanjeZoblaka = 200;
// rezultati
var zalijevanjaMjesecno = Math.ceil(30 / frekvencija);
var satiMjesecno = Math.ceil(vrijeme) * zalijevanjaMjesecno;
var plata = satiMjesecno * satnica;
var iznosGoriva = Math.ceil((potrosnja / 100) * bazaCijeneGoriva * (udaljenost * 2) * zalijevanjaMjesecno);
var ukupnoTroskovaMjesecno = iznosGoriva + plata;
var trosakZoblaka = bazaCijeneZoblaka + godisnjeOdrzavanjeZoblaka * (brojSezona - 1);
var satiUProjekciji = satiMjesecno * sezona * brojSezona;
var novcaUProjekciji = ukupnoTroskovaMjesecno * sezona * brojSezona;
var iznosUstede = novcaUProjekciji - trosakZoblaka
var vrijemeZaBreakEven = Math.ceil(bazaCijeneZoblaka / ukupnoTroskovaMjesecno);
$('#vrijemeZaBreakEven').html(vrijemeZaBreakEven);
$('#brojSezona').html(brojSezona);
$('#iznosUstede').html(iznosUstede);
$('#satiUProjekciji').html(satiUProjekciji);
$('#iznosGoriva').html(iznosGoriva);
$('#satiMjesecno').html(satiMjesecno);
$('#plata').html(plata);
$('#ukupnoTroskovaMjesecno').html(ukupnoTroskovaMjesecno);
$('#bazaCijeneZoblaka').html(bazaCijeneZoblaka);
};
$(document).ready(function() {
iskalkulisi();
$(".ulazni-parametar").keyup(iskalkulisi);
$(".ulazni-parametar").change(iskalkulisi);
});
</script>
</body>
</html>

BIN
misc/zoblak.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB