Commented all methods and added max temperature for plant types.

This commit is contained in:
Andrés Uribe Stengel 2020-07-30 13:30:03 +02:00
parent 4bb46704cb
commit 2255436a2f
9 changed files with 80 additions and 16 deletions

View File

@ -3,6 +3,8 @@ import { Meteor } from 'meteor/meteor';
import ReactDOM from 'react-dom';
import App from '../imports/ui/App';
import 'bootstrap/dist/css/bootstrap.min.css';
import startMqttObserver from "../mongo-mqtt";
import {publish} from "../imports/api/mqttApi";
export const PlantTypesCollection = new Meteor.Collection('plantTypes');
export const SensorDataCollection = new Meteor.Collection('sensorData');
@ -10,6 +12,8 @@ export const ActiveDeviceCollection = new Meteor.Collection('activeDevice');
export const ConfiguredDevicesCollection = new Meteor.Collection('configuredDevices');
Meteor.startup(() => {
// If this code is on the meteor server-side, publish all MongoDB collections for the client-side,
// start the mqtt-observer script to push data to the database and define the mqttPublish method to be accessible from the client-side.
if(Meteor.isServer) {
Meteor.publish('plantTypesCollection', function() {
return PlantTypesCollection.find();
@ -26,8 +30,17 @@ Meteor.startup(() => {
Meteor.publish('configuredDevicesCollection', function() {
return ConfiguredDevicesCollection.find();
})
startMqttObserver()
Meteor.methods({
'mqttPublish'({ topic, payload }) {
publish(topic, payload)
}
})
}
// If this code is on the meteor client-side, subscribe all published MongoDB collections from the server-side and render the application.
if (Meteor.isClient) {
Meteor.subscribe('plantTypesCollection');
Meteor.subscribe('sensorDataCollection');

View File

@ -1,5 +1,7 @@
import {SensorDataCollection} from "../../client/main";
// Fetch all esp-IDs stored in all documents of the sensorData collection and remove duplicates,
// returning an array of esp-IDs that have sent data to the database.
export function getAllEspIds() {
return _.uniq(SensorDataCollection.find({}, {
sort: {device_id: 1}, fields: {device_id: true}

View File

@ -2,6 +2,7 @@ import { connect } from 'mqtt';
var client = connect('mqtt://mqtt.timovolkmann.de')
// If connection to the mqtt-client is not established, try to reconnect. Else publish a message to the mqtt broker.
function publish(topic, message) {
if (!client.connected) {
client.reconnect();

View File

@ -1,5 +1,6 @@
import {PlantTypesCollection} from '../../client/main'
// Return an array of all plant types currently stored in the database.
export function getAllPlantTypes() {
const plantTypesDocuments = PlantTypesCollection.find();

View File

@ -5,30 +5,42 @@ import { Table } from "react-bootstrap";
import Settings from "./Settings";
export default function Home() {
// Return all documents of the configuredDevices collection.
const configuredDevices = useTracker(() => {
return ConfiguredDevicesCollection.find().fetch();
});
// Return the soil type of the given plant type.
function getSoil(type) {
const plantType = PlantTypesCollection.findOne({plantType: type});
return plantType.dirtType
}
// Return the permanent wilting point of the given plant type.
function getPermWilPoint(type) {
const plantType = PlantTypesCollection.findOne({plantType: type});
return plantType.data.soilMoisture.pwp
}
// Return the field capacity of the given plant type.
function getFieldCap(type) {
const plantType = PlantTypesCollection.findOne({plantType: type});
return plantType.data.soilMoisture.fc
}
// Return the minimal temperature of the given plant type.
function getMinTemp(type) {
const plantType = PlantTypesCollection.findOne({plantType: type});
return plantType.data.temperature.minTemp
}
// Return the maximal temperature of the given plant type.
function getMaxTemp(type) {
const plantType = PlantTypesCollection.findOne({plantType: type});
return plantType.data.temperature.maxTemp
}
// Return the minimal lux value of the given plant type.
function getMinLux(type) {
const plantType = PlantTypesCollection.findOne({plantType: type});
return plantType.data.light.minLux
@ -48,7 +60,8 @@ export default function Home() {
<th key={'mode'}>Mode</th>
<th key={'pwp'}>Permanent Wilting Point</th>
<th key={'fc'}>Field Capacity</th>
<th key={'temp'}>Min Temperature</th>
<th key={'minTemp'}>Min Temperature</th>
<th key={'maxTemp'}>Max Temperature</th>
<th key={'brightness'}>Min Brightness</th>
</tr>
</thead>
@ -63,7 +76,8 @@ export default function Home() {
<td key={'mode-' + index}>{device.mode}</td>
<td key={'pwp-' + index}>{getPermWilPoint(device.type) + " %"}</td>
<td key={'fc-' + index}>{getFieldCap(device.type) + " %"}</td>
<td key={'temp-' + index}>{getMinTemp(device.type) + " °C"}</td>
<td key={'minTemp-' + index}>{getMinTemp(device.type) + " °C"}</td>
<td key={'maxTemp-' + index}>{getMaxTemp(device.type) + " °C"}</td>
<td key={'brightness-' + index}>{getMinLux(device.type) + " lux"}</td>
</tr>
})}

View File

@ -5,18 +5,20 @@ import {SensorDataCollection, ActiveDeviceCollection, ConfiguredDevicesCollectio
import {useTracker} from 'meteor/react-meteor-data';
import { Col, Form, Row, Card, CardDeck } from "react-bootstrap";
import moment from 'moment'
import { scaleLog } from 'd3-scale';
const scale = scaleLog().base(Math.E);
export default function Overview() {
// Return the document of the currently active device in the activeDevice collection.
const activeDevice = useTracker(() => {
return ActiveDeviceCollection.find().fetch()[0];
});
// Return all documents of the configuredDevices collection.
const configuredDevices = useTracker(() => {
return ConfiguredDevicesCollection.find().fetch();
});
// If the activeDevice is not null or undefined, return and filter the documents of the last two days (48 Hrs) of the currently active device in the sensorData collection
// and reverse the array so that the oldest documents come first.
const sensorData = useTracker(() => {
if (activeDevice === null || activeDevice === undefined) {
return [];
@ -33,6 +35,7 @@ export default function Overview() {
}
});
// Set the selected device from the configured devices dropdown-menu, to the new currently active device.
const handleChange = (e) => {
if (e.target.value === "") {
console.log("No device selected!");
@ -42,11 +45,12 @@ export default function Overview() {
}
}
// Get the current date as string.
const getLabelFromStamp = (x) => {
return moment(x.timestamp).format("LLLL");
// return moment(x.timestamp).format("HH:mm");
}
// Workaround for log(0).
const brightnessMapper = (x) => {
if (x.brightness < 1) {
x.brightness = 0.1
@ -54,6 +58,7 @@ export default function Overview() {
return x;
}
//If the data that is loaded from the database has not been fetched yet, show a loading screen. Else load the application.
if ((sensorData.length <= 0)) {
return (
<>
@ -77,7 +82,8 @@ export default function Overview() {
</Form>
</Col>
<Col>
<h6>active device: {!activeDevice === undefined && !ConfiguredDevicesCollection.findOne({ deviceId: activeDevice.deviceId }) === undefined ? ConfiguredDevicesCollection.findOne({ deviceId: activeDevice.deviceId }).alias : "No device selected"}</h6>
<h6>active device: {!activeDevice === undefined && !ConfiguredDevicesCollection.findOne({ deviceId: activeDevice.deviceId }) === undefined ?
ConfiguredDevicesCollection.findOne({ deviceId: activeDevice.deviceId }).alias : "No device selected"}</h6>
</Col>
</Row>
@ -114,7 +120,8 @@ export default function Overview() {
</Form>
</Col>
<Col>
<h6>active device: {ConfiguredDevicesCollection.findOne({deviceId: activeDevice.deviceId}) === undefined ? "No device selected" : ConfiguredDevicesCollection.findOne({deviceId: activeDevice.deviceId}).alias}</h6>
<h6>active device: {ConfiguredDevicesCollection.findOne({deviceId: activeDevice.deviceId}) === undefined ?
"No device selected" : ConfiguredDevicesCollection.findOne({deviceId: activeDevice.deviceId}).alias}</h6>
</Col>
</Row>

View File

@ -9,10 +9,12 @@ import {
import {useTracker} from 'meteor/react-meteor-data';
export default function SensorCardDeck() {
// Return the document of the currently active device in the activeDevice collection.
const deviceId = useTracker(() => {
return ActiveDeviceCollection.find().fetch()[0];
});
// If the deviceId is not null or undefined, return the most recent document in the sensorData collection of the currently active device.
const sensorData = useTracker(() => {
if (deviceId === null || deviceId === undefined) {
return undefined;
@ -21,7 +23,8 @@ export default function SensorCardDeck() {
}
});
const plantType = useTracker(() => {
// If the deviceId is not null or undefined, return the document in the configuredDevices collection of the currently active device.
const configurationData = useTracker(() => {
if (deviceId === null || deviceId === undefined) {
return undefined;
} else {
@ -29,16 +32,18 @@ export default function SensorCardDeck() {
}
});
// If the configurationData is not null or undefined, return the configured plant type of the currently active device.
const plantTypeData = useTracker(() => {
if (plantType === null || plantType === undefined) {
if (configurationData === null || configurationData === undefined) {
return undefined;
} else {
return PlantTypesCollection.findOne({plantType: plantType.type});
return PlantTypesCollection.findOne({plantType: configurationData.type});
}
});
if (sensorData.length <= 0 || plantType === undefined || deviceId === undefined || plantTypeData === undefined) {
//If the data that is loaded from the database has not been fetched yet, show a loading screen. Else load the application.
if (sensorData.length <= 0 || configurationData === undefined || deviceId === undefined || plantTypeData === undefined) {
return (
<CardDeck>
<Card>

View File

@ -7,21 +7,25 @@ import {ConfiguredDevicesCollection, PlantTypesCollection} from "../../client/ma
import {useTracker} from 'meteor/react-meteor-data';
export default function Settings() {
// Return all documents of the plantTypes collection.
const plantTypes = useTracker(() => {
return PlantTypesCollection.find();
});
const espIds = useTracker(getAllEspIds);
// Variables for the configuration of the selected device.
var selectedEspId;
var selectedAlias = "";
var selectedType;
var selectedMode = null;
// Settings from the database for each plant type to be published to the mqtt broker.
const payloadVegi = plantTypes.fetch()[0];
const payloadCacti = plantTypes.fetch()[1];
const payloadFlower = plantTypes.fetch()[2];
// If the Name field is empty, log a warning. Else set the entered string as selectedAlias.
const handleChangeAlias = (e) => {
if (e.target.value === "") {
console.log("Nickname cannot be empty!");
@ -30,6 +34,7 @@ export default function Settings() {
}
}
// If no device is selected, log a warning. Else set the selected device as selectedEspId.
const handleChangeDevice = (e) => {
if (e.target.value === "") {
console.log("No device selected!");
@ -38,6 +43,7 @@ export default function Settings() {
}
}
// If no plant type is selected, log a warning. Else set the selected type as selectedType.
const handleChangeType = (e) => {
if (e.target.value === "") {
console.log("No type selected!");
@ -46,36 +52,45 @@ export default function Settings() {
}
}
// If no mode is selected, log a warning. Else set the selected mode as selectedMode.
const handleChangeMode = (e) => {
if (e.target.value === "") {
console.log("No type selected!");
console.log("No mode selected!");
} else {
selectedMode = e.target.value;
}
}
// Save the configured device to the database and send the payload data to the mqtt broker.
const handleSendData = (e) => {
var payload = "";
var nmValue = null;
var minLux = PlantTypesCollection.findOne({plantType: selectedType}).data.light.minLux;
// Configure the payload depending on the selected plant type and the selected mode.
if (selectedType === "Vegetables") {payload = JSON.stringify(payloadVegi.data.soilMoisture);}
if (selectedType === "Cacti") {payload = JSON.stringify(payloadCacti.data.soilMoisture);}
if (selectedType === "Flowers") {payload = JSON.stringify(payloadFlower.data.soilMoisture);}
if (selectedMode === "Growth") {nmValue = 450}
if (selectedMode === "Bloom") {nmValue = 700}
// Log all data
console.log("Payload: " + payload);
console.log("Alias: " + selectedAlias);
console.log("ID: " + selectedEspId);
console.log("Type: " + selectedType);
console.log("Mode: " + selectedMode);
if ((payload === "") || (nmValue === null) || (selectedEspId === undefined) || (selectedAlias === "") || (selectedType === undefined) || (selectedMode === null)) {alert("Nickname is empty or no device/type/mode selected!");} else {
// If one of the values is empty, null or undefined send an alert. Else insert a document in the configuredDevices collection with the entered data,
// or update the data if the device has already been configured.
if ((payload === "") || (nmValue === null) || (minLux === undefined) || (selectedEspId === undefined) ||
(selectedAlias === "") || (selectedType === undefined) || (selectedMode === null)) {alert("Nickname is empty or no device/type/mode selected!");} else {
var doc = ConfiguredDevicesCollection.findOne({deviceId: selectedEspId});
if (doc === undefined) {ConfiguredDevicesCollection.insert({deviceId: selectedEspId, alias: selectedAlias, type: selectedType, mode: selectedMode});} else {
ConfiguredDevicesCollection.update({_id: doc._id}, {$set: {alias: selectedAlias, type: selectedType, mode: selectedMode}});
}
// Publish the configuration data of the selected plant type for the selected device to the mqtt broker.
Meteor.call('mqttPublish', {
topic: 'smartgarden/commands/' + selectedEspId + '/soil',
payload: payload
@ -87,6 +102,7 @@ export default function Settings() {
}
})
// Publish the irrigation and light values as true to the mqtt broker, to activate the automatic irrigation and lighting for the selected device.
Meteor.call('mqttPublish', {
topic: 'smartgarden/commands/' + selectedEspId + '/automatic',
payload: JSON.stringify({
@ -101,10 +117,11 @@ export default function Settings() {
}
})
// Publish the minimum lux value of the selected plant type and the nanometer value of the selected mode for the selected device to the mqtt broker.
Meteor.call('mqttPublish', {
topic: 'smartgarden/commands/' + selectedEspId + '/light',
payload: JSON.stringify({
'minLX': 500,
'minLX': minLux,
'nm': nmValue
})
}, (err, res) => {
@ -118,6 +135,7 @@ export default function Settings() {
}
//If the data that is loaded from the database has not been fetched yet, show a loading screen. Else load the application.
if ((plantTypes.length <= 0) || (espIds.length <= 0)) {
return (
<CardDeck>

View File

@ -8,6 +8,8 @@ var ActiveDeviceCollection = new Meteor.Collection('activeDevice');
var ConfiguredDevicesCollection = new Meteor.Collection('configuredDevices');
Meteor.startup(() => {
// If this code is on the meteor server-side, publish all MongoDB collections for the client-side,
// start the mqtt-observer script to push data to the database and define the mqttPublish method to be accessible from the client-side.
if(Meteor.isServer) {
Meteor.publish('plantTypesCollection', function() {
return PlantTypesCollection.find();
@ -25,7 +27,7 @@ Meteor.startup(() => {
return ConfiguredDevicesCollection.find();
})
startMqttObserver()
//startMqttObserver()
Meteor.methods({
'mqttPublish'({ topic, payload }) {
@ -34,6 +36,7 @@ Meteor.startup(() => {
})
}
// If this code is on the meteor client-side, subscribe all published MongoDB collections from the server-side and render the application.
if (Meteor.isClient) {
Meteor.subscribe('plantTypesCollection');
Meteor.subscribe('sensorDataCollection');