created and tested server side water control
This commit is contained in:
@@ -22,3 +22,5 @@ less
|
|||||||
huttonr:bootstrap3
|
huttonr:bootstrap3
|
||||||
nimble:restivus
|
nimble:restivus
|
||||||
momentjs:moment
|
momentjs:moment
|
||||||
|
selaias:meteor-simpleweather
|
||||||
|
u2622:persistent-session
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
accounts-base@1.2.2
|
accounts-base@1.2.2
|
||||||
|
amplify@1.0.0
|
||||||
autopublish@1.0.4
|
autopublish@1.0.4
|
||||||
autoupdate@1.2.4
|
autoupdate@1.2.4
|
||||||
babel-compiler@5.8.24_1
|
babel-compiler@5.8.24_1
|
||||||
@@ -27,6 +28,7 @@ ejson@1.0.7
|
|||||||
es5-shim@4.1.14
|
es5-shim@4.1.14
|
||||||
fastclick@1.0.7
|
fastclick@1.0.7
|
||||||
geojson-utils@1.0.4
|
geojson-utils@1.0.4
|
||||||
|
handlebars@1.0.4
|
||||||
hot-code-push@1.0.0
|
hot-code-push@1.0.0
|
||||||
html-tools@1.0.5
|
html-tools@1.0.5
|
||||||
htmljs@1.0.5
|
htmljs@1.0.5
|
||||||
@@ -36,6 +38,7 @@ huttonr:bootstrap3-assets@3.3.5_6
|
|||||||
id-map@1.0.4
|
id-map@1.0.4
|
||||||
insecure@1.0.4
|
insecure@1.0.4
|
||||||
jquery@1.11.4
|
jquery@1.11.4
|
||||||
|
json@1.0.3
|
||||||
launch-screen@1.0.4
|
launch-screen@1.0.4
|
||||||
less@2.5.1
|
less@2.5.1
|
||||||
livedata@1.0.15
|
livedata@1.0.15
|
||||||
@@ -62,6 +65,7 @@ reactive-var@1.0.6
|
|||||||
reload@1.1.4
|
reload@1.1.4
|
||||||
retry@1.0.4
|
retry@1.0.4
|
||||||
routepolicy@1.0.6
|
routepolicy@1.0.6
|
||||||
|
selaias:meteor-simpleweather@0.6.8
|
||||||
service-configuration@1.0.5
|
service-configuration@1.0.5
|
||||||
session@1.1.1
|
session@1.1.1
|
||||||
simple:json-routes@1.0.4
|
simple:json-routes@1.0.4
|
||||||
@@ -71,6 +75,7 @@ standard-minifiers@1.0.2
|
|||||||
templating@1.1.5
|
templating@1.1.5
|
||||||
templating-tools@1.0.0
|
templating-tools@1.0.0
|
||||||
tracker@1.0.9
|
tracker@1.0.9
|
||||||
|
u2622:persistent-session@0.4.4
|
||||||
ui@1.0.8
|
ui@1.0.8
|
||||||
underscore@1.0.4
|
underscore@1.0.4
|
||||||
url@1.0.5
|
url@1.0.5
|
||||||
|
|||||||
@@ -1,25 +1,20 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>TFM</title>
|
<title>TFM</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="author" content="Amir Smajevic, Adnan Strojil, Senad Uka">
|
<meta name="author" content="Amir Smajevic, Adnan Strojil, Senad Uka">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<ul class="nav nav-tabs">
|
{{> tabs}}
|
||||||
<li role="presentation" class="active"><a href="#">Početak</a></li>
|
|
||||||
<li role="presentation"><a href="#">Novosti</a></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="hello">
|
{{> Template.dynamic template=template_name }}
|
||||||
{{> display}}
|
</div>
|
||||||
</div>
|
|
||||||
</div> <!-- /container -->
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
<!-- /container -->
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|||||||
@@ -1,17 +1,8 @@
|
|||||||
if (Meteor.isClient) {
|
// at the beginning
|
||||||
|
Session.set("templateName", "start");
|
||||||
|
|
||||||
Template.display.helpers({
|
Template.body.helpers({
|
||||||
sensorDataCollection: function () {
|
template_name: function() {
|
||||||
return SensorData.find({}, {sort: {created_at: -1}})
|
return Session.get("templateName");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Template.display.events({
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.sensorData.helpers({
|
|
||||||
created_at_formatted: function() {
|
|
||||||
return moment(this.created_at).format("DD.MM.YYYY, HH:mm")
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,3 +2,7 @@
|
|||||||
padding: 40px 15px;
|
padding: 40px 15px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.controller_selection {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
<template name="display">
|
|
||||||
<ul>
|
|
||||||
{{#each sensorDataCollection}}
|
|
||||||
{{> sensorData}}
|
|
||||||
{{/each}}
|
|
||||||
</ul>
|
|
||||||
</template>
|
|
||||||
13
app/client/log.html
Normal file
13
app/client/log.html
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<template name="log">
|
||||||
|
|
||||||
|
<div class="hello">
|
||||||
|
<h1> Podaci sa senzora: </h1>
|
||||||
|
<button id="clear_log">Očisti</button>
|
||||||
|
<ul>
|
||||||
|
{{#each sensorDataCollection}}
|
||||||
|
{{> sensorData}}
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
21
app/client/log.js
Normal file
21
app/client/log.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
Template.log.helpers({
|
||||||
|
sensorDataCollection: function() {
|
||||||
|
return SensorData.find({}, {
|
||||||
|
sort: {
|
||||||
|
created_at: -1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.log.events({
|
||||||
|
'click clear_log': function() {
|
||||||
|
Meteor.call('clearLog');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.sensorData.helpers({
|
||||||
|
created_at_formatted: function() {
|
||||||
|
return moment(this.created_at).format("DD.MM.YYYY, HH:mm")
|
||||||
|
}
|
||||||
|
});
|
||||||
5
app/client/start.html
Normal file
5
app/client/start.html
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<template name="start">
|
||||||
|
<div class="col-lg-12 text-center">
|
||||||
|
{{>state}}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
17
app/client/start.js
Normal file
17
app/client/start.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
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+'°'+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
|
||||||
18
app/client/state.html
Normal file
18
app/client/state.html
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<template name="state">
|
||||||
|
|
||||||
|
<h1> Controller state</h1>
|
||||||
|
<div>
|
||||||
|
{{#with controller_state}}
|
||||||
|
<div>Controller broj: {{controller_id}}</div>
|
||||||
|
<div>Otpusni ventil: {{pretty_valve state.out_valve }}</div>
|
||||||
|
<div>Stanje postavio: {{ set_by }}</div>
|
||||||
|
<div>Zadnja komunikacija: {{time}}</div>
|
||||||
|
{{/with}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<button id="water_now">Zalij sada</button>
|
||||||
|
<button id="stop_water_now">Prekini zalijevanje</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
52
app/client/state.js
Normal file
52
app/client/state.js
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
if (Meteor.isClient) {
|
||||||
|
|
||||||
|
function controller_state() {
|
||||||
|
var controller = Session.get('controller_id');
|
||||||
|
var result = {}
|
||||||
|
if (controller) {
|
||||||
|
result = ControllerState.findOne({
|
||||||
|
controller_id: controller
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
result = {}
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
Template.state.helpers({
|
||||||
|
controller_state: controller_state,
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.state.events({
|
||||||
|
'click #water_now': function() {
|
||||||
|
var state = controller_state();
|
||||||
|
ControllerState.update(state._id, {
|
||||||
|
'$set': {
|
||||||
|
'state.out_valve': 'opening',
|
||||||
|
'time': new Date(),
|
||||||
|
'set_by': 'server'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'click #stop_water_now': function() {
|
||||||
|
var state = controller_state();
|
||||||
|
ControllerState.update(state._id, {
|
||||||
|
'$set': {
|
||||||
|
'state.out_valve': 'closing',
|
||||||
|
'time': new Date(),
|
||||||
|
'set_by': 'server'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
10
app/client/tabs.html
Normal file
10
app/client/tabs.html
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<template name="tabs">
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<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="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>
|
||||||
|
|
||||||
|
</template>
|
||||||
39
app/client/tabs.js
Normal file
39
app/client/tabs.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
if (Meteor.isClient) {
|
||||||
|
|
||||||
|
Template.tabs.helpers({
|
||||||
|
class_for: function(tab_name) {
|
||||||
|
var templateName = Session.get('templateName');
|
||||||
|
|
||||||
|
if (templateName === tab_name) {
|
||||||
|
return tab_name + ' active';
|
||||||
|
} else if (templateName === 'display' && tab_name === 'news') {
|
||||||
|
return tab_name + ' active'
|
||||||
|
} else {
|
||||||
|
return tab_name;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
selected_controller: function() {
|
||||||
|
return Session.get('controller_id');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.tabs.events({
|
||||||
|
'click .start': function() {
|
||||||
|
Session.set('templateName', 'start');
|
||||||
|
},
|
||||||
|
'click .weather': function() {
|
||||||
|
Session.set('templateName', 'weather');
|
||||||
|
},
|
||||||
|
'click .log': function() {
|
||||||
|
Session.set('templateName', 'log');
|
||||||
|
},
|
||||||
|
|
||||||
|
'click #switch': function() {
|
||||||
|
var instance = Template.instance();
|
||||||
|
controller_id = instance.$('#controller').val();
|
||||||
|
Session.setPersistent('controller_id', controller_id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
|
|
||||||
SensorData = new Mongo.Collection("sensorData");
|
SensorData = new Mongo.Collection("sensorData");
|
||||||
|
ControllerState = new Mongo.Collection("controller_states");
|
||||||
|
|||||||
61
app/server/api.js
Normal file
61
app/server/api.js
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
// 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),
|
||||||
|
owner: this.bodyParams.owner,
|
||||||
|
created_at: new Date()
|
||||||
|
});
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Api.addRoute('state/:id', {
|
||||||
|
authRequired: false
|
||||||
|
}, {
|
||||||
|
post: function() {
|
||||||
|
console.log("Body params", this.bodyParams);
|
||||||
|
return ControllerState.update({
|
||||||
|
controller_id: this.urlParams.id
|
||||||
|
}, {
|
||||||
|
'$set': {
|
||||||
|
'state.out_valve': this.bodyParams.out_valve,
|
||||||
|
'time': new Date(),
|
||||||
|
'set_by': 'client'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
get: function() {
|
||||||
|
return stateOrDefault(this.urlParams.id).state;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function stateOrDefault(id) {
|
||||||
|
var stateEntry = ControllerState.findOne({
|
||||||
|
controller_id: id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (stateEntry === undefined) {
|
||||||
|
stateEntry = ControllerState.insert({
|
||||||
|
controller_id: id,
|
||||||
|
state: {
|
||||||
|
out_valve: 'closed'
|
||||||
|
},
|
||||||
|
time: new Date(),
|
||||||
|
set_by: 'server'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var stateEntry = ControllerState.findOne({
|
||||||
|
controller_id: id,
|
||||||
|
});
|
||||||
|
return stateEntry;
|
||||||
|
}
|
||||||
@@ -1,6 +1,12 @@
|
|||||||
if (Meteor.isServer) {
|
if (Meteor.isServer) {
|
||||||
Meteor.startup(function() {
|
Meteor.startup(function() {
|
||||||
// code to run on server at startup
|
// code to run on server at startup
|
||||||
|
|
||||||
|
return Meteor.methods({
|
||||||
|
clearLog: function() {
|
||||||
|
return SensorData.remove({});
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Global API configuration
|
// Global API configuration
|
||||||
@@ -15,11 +21,13 @@ if (Meteor.isServer) {
|
|||||||
post: function() {
|
post: function() {
|
||||||
SensorData.insert({
|
SensorData.insert({
|
||||||
temperatureValue: parseFloat(this.bodyParams.temperatureValue),
|
temperatureValue: parseFloat(this.bodyParams.temperatureValue),
|
||||||
humidityValue: parseFloat(this.bodyParams.humidityValue),
|
humidityValue: parseFloat(this.bodyParams.humidityValue),
|
||||||
owner: this.bodyParams.owner,
|
owner: this.bodyParams.owner,
|
||||||
created_at: new Date()
|
created_at: new Date()
|
||||||
});
|
});
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
import sys
|
|
||||||
import requests
|
|
||||||
import Adafruit_DHT
|
|
||||||
|
|
||||||
|
|
||||||
GPIO_PIN = 4
|
|
||||||
SENSORDATA_URL = 'http://tfm.meteor.com/api/sensorData'
|
|
||||||
SENSOR_TYPE = Adafruit_DHT.DHT11
|
|
||||||
|
|
||||||
if len(sys.argv) == 2:
|
|
||||||
owner = sys.argv[1]
|
|
||||||
else:
|
|
||||||
print 'usage: sudo ./controller.py [OWNER]#'
|
|
||||||
print 'example: sudo ./controller.py Senad - Send temperature as Senad'
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
# Try to grab a sensor reading. Use the read_retry method which will retry up
|
|
||||||
# to 15 times to get a sensor reading (waiting 2 seconds between each retry).
|
|
||||||
humidity, temperature = Adafruit_DHT.read_retry(SENSOR_TYPE, GPIO_PIN)
|
|
||||||
|
|
||||||
# Un-comment the line below to convert the temperature to Fahrenheit.
|
|
||||||
# temperature = temperature * 9/5.0 + 32
|
|
||||||
|
|
||||||
# Note that sometimes you won't get a reading and
|
|
||||||
# the results will be null (because Linux can't
|
|
||||||
# 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(SENSORDATA_URL, json={"owner": owner, "temperatureValue": temperature, "humidityValue":humidity})
|
|
||||||
print 'Temp={0:0.1f}*C'.format(temperature)
|
|
||||||
print 'Humidity={0:0.1f}%'.format(humidity)
|
|
||||||
if response.status_code != 200:
|
|
||||||
print 'Failed to send temperature!'
|
|
||||||
sys.exit(2)
|
|
||||||
else:
|
|
||||||
print 'Failed to get reading. Try again!'
|
|
||||||
sys.exit(1)
|
|
||||||
@@ -1 +1,2 @@
|
|||||||
requests==2.4.3
|
requests==2.4.3
|
||||||
|
rpi.gpio==0.6.0a3
|
||||||
|
|||||||
Reference in New Issue
Block a user