├── .gitignore ├── README.md ├── models ├── user.model.js ├── plant.model.js └── moistureMeasurement.model.js ├── routes ├── users.js ├── plants.js └── moistureMeasurements.js ├── LICENSE ├── package.json └── server.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | .env -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # smart-garden-backend 2 | 3 | > The backend for Smart Garden. 4 | 5 | ## Quick Start 6 | 7 | 1. Clone or download the repo 8 | 2. Run `npm install` 9 | 3. Run `nodemon server` 10 | -------------------------------------------------------------------------------- /models/user.model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const Schema = mongoose.Schema; 4 | 5 | const userSchema = new Schema( 6 | { 7 | username: { 8 | type: String, 9 | required: true, 10 | unique: true, 11 | trim: true, 12 | minlength: 3, 13 | }, 14 | }, 15 | { 16 | timestamps: true, 17 | } 18 | ); 19 | 20 | const User = mongoose.model("User", userSchema); 21 | 22 | module.exports = User; 23 | -------------------------------------------------------------------------------- /models/plant.model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const Schema = mongoose.Schema; 4 | 5 | const plantSchema = new Schema( 6 | { 7 | username: { 8 | type: String, 9 | required: true, 10 | }, 11 | plantName: { 12 | type: String, 13 | required: true, 14 | }, 15 | }, 16 | { 17 | timestamps: true, 18 | } 19 | ); 20 | 21 | const Plant = mongoose.model("Plant", plantSchema); 22 | 23 | module.exports = Plant; 24 | -------------------------------------------------------------------------------- /routes/users.js: -------------------------------------------------------------------------------- 1 | const router = require("express").Router(); 2 | let User = require("../models/user.model"); 3 | 4 | // view all users 5 | router.route("/").get((req, res) => { 6 | User.find() 7 | .then((users) => res.json(users)) 8 | .catch((err) => res.status(400).json("Error: " + err)); 9 | }); 10 | 11 | // add new user 12 | router.route("/add").post((req, res) => { 13 | const username = req.body.username; 14 | 15 | const newUser = new User({ username }); 16 | 17 | newUser 18 | .save() 19 | .then(() => res.json("User added!")) 20 | .catch((err) => res.status(400).json("Error: " + err)); 21 | }); 22 | 23 | module.exports = router; 24 | -------------------------------------------------------------------------------- /models/moistureMeasurement.model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const Schema = mongoose.Schema; 4 | 5 | const moistureMeasurementSchema = new Schema( 6 | { 7 | username: { 8 | type: String, 9 | required: true, 10 | }, 11 | plantName: { 12 | type: String, 13 | required: true, 14 | }, 15 | moistureReading: { 16 | type: Number, 17 | required: true, 18 | }, 19 | date: { 20 | type: Date, 21 | required: true, 22 | }, 23 | }, 24 | { 25 | timestamps: true, 26 | } 27 | ); 28 | 29 | const MoistureMeasurement = mongoose.model( 30 | "MoistureMeasurement", 31 | moistureMeasurementSchema 32 | ); 33 | 34 | module.exports = MoistureMeasurement; 35 | -------------------------------------------------------------------------------- /routes/plants.js: -------------------------------------------------------------------------------- 1 | const router = require("express").Router(); 2 | let Plant = require("../models/plant.model"); 3 | 4 | // view all plants 5 | router.route("/").get((req, res) => { 6 | Plant.find() 7 | .then((plants) => res.json(plants)) 8 | .catch((err) => res.status(400).json("Error: " + err)); 9 | }); 10 | 11 | // view all plants for a specific user 12 | router.route("/:username").get((req, res) => { 13 | Plant.find({ username: req.params.username }) 14 | .then((plants) => res.json(plants)) 15 | .catch((err) => res.status(400).json("Error: " + err)); 16 | }); 17 | 18 | // add new plant 19 | router.route("/add").post((req, res) => { 20 | const username = req.body.username; 21 | const plantName = req.body.plantName; 22 | 23 | const newPlant = new Plant({ username, plantName }); 24 | 25 | newPlant 26 | .save() 27 | .then(() => res.json("Plant added!")) 28 | .catch((err) => res.status(400).json("Error: " + err)); 29 | }); 30 | 31 | module.exports = router; 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Lauren Stephenson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "smart-garden-backend", 3 | "version": "1.0.0", 4 | "description": "The backend for Smart Garden.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "prettier": "prettier --write '**/*.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/CompSciLauren/smart-garden-backend.git" 13 | }, 14 | "keywords": [], 15 | "author": "", 16 | "license": "ISC", 17 | "bugs": { 18 | "url": "https://github.com/CompSciLauren/smart-garden-backend/issues" 19 | }, 20 | "homepage": "https://github.com/CompSciLauren/smart-garden-backend#readme", 21 | "dependencies": { 22 | "@serialport/parser-readline": "^9.0.1", 23 | "axios": "^0.20.0", 24 | "cors": "^2.8.5", 25 | "dotenv": "^8.2.0", 26 | "express": "^4.17.1", 27 | "luxon": "^1.25.0", 28 | "mongoose": "^5.10.7", 29 | "serialport": "^9.0.1" 30 | }, 31 | "devDependencies": { 32 | "husky": "^4.3.0", 33 | "lint-staged": "^10.4.0", 34 | "prettier": "^2.1.2" 35 | }, 36 | "husky": { 37 | "hooks": { 38 | "pre-commit": "lint-staged" 39 | } 40 | }, 41 | "lint-staged": { 42 | "*.{js,css,md}": "prettier --write" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const cors = require("cors"); 3 | const mongoose = require("mongoose"); 4 | const axios = require("axios"); 5 | const SerialPort = require("serialport"); 6 | const Readline = require("@serialport/parser-readline"); 7 | 8 | require("dotenv").config(); 9 | const username = process.env.MYUSERNAME; 10 | const plantName = process.env.MYPLANTNAME; 11 | 12 | // read data from sensor 13 | const serialPort = new SerialPort("COM3"); 14 | const parser = serialPort.pipe(new Readline({ delimiter: "\r\n" })); 15 | 16 | let shouldCaptureData = false; 17 | 18 | parser.on("data", (data) => { 19 | if (shouldCaptureData) { 20 | console.log("Saving data", data); 21 | 22 | const dataObject = { 23 | username: username, 24 | plantName: plantName, 25 | moistureReading: data.toString(), 26 | }; 27 | 28 | axios 29 | .post("http://localhost:5000/moistureMeasurements/add", dataObject) 30 | .then((res) => console.log(res.data)) 31 | .catch(function (error) { 32 | console.log(error); 33 | }); 34 | shouldCaptureData = false; 35 | } 36 | }); 37 | 38 | let minutes = 10; 39 | let interval = minutes * 60 * 1000; 40 | 41 | setInterval(() => { 42 | shouldCaptureData = true; 43 | }, interval); 44 | 45 | const app = express(); 46 | const port = process.env.PORT || 5000; 47 | 48 | app.use(cors()); 49 | app.use(express.json()); 50 | 51 | const uri = process.env.ATLAS_URI; 52 | mongoose.connect(uri, { 53 | useNewUrlParser: true, 54 | useCreateIndex: true, 55 | useUnifiedTopology: true, 56 | }); 57 | const connection = mongoose.connection; 58 | connection.once("open", () => { 59 | console.log("MongoDB database connection established successfully"); 60 | }); 61 | 62 | // add routers 63 | const usersRouter = require("./routes/users"); 64 | const plantsRouter = require("./routes/plants"); 65 | const moistureMeasurementsRouter = require("./routes/moistureMeasurements"); 66 | app.use("/users", usersRouter); 67 | app.use("/plants", plantsRouter); 68 | app.use("/moistureMeasurements", moistureMeasurementsRouter); 69 | 70 | app.listen(port, () => { 71 | console.log(`Server is running on port: ${port}`); 72 | }); 73 | -------------------------------------------------------------------------------- /routes/moistureMeasurements.js: -------------------------------------------------------------------------------- 1 | const router = require("express").Router(); 2 | let MoistureMeasurement = require("../models/moistureMeasurement.model"); 3 | const { DateTime } = require("luxon"); 4 | 5 | // view all moisture measurements 6 | router.route("/").get((req, res) => { 7 | MoistureMeasurement.find() 8 | .then((moistureMeasurements) => res.json(moistureMeasurements)) 9 | .catch((err) => res.status(400).json("Error: " + err)); 10 | }); 11 | 12 | // view all moisture measurements for a specific plant 13 | router.route("/:username/:plantName").get((req, res) => { 14 | MoistureMeasurement.find({ 15 | username: req.params.username, 16 | plantName: req.params.plantName, 17 | }) 18 | .then((moistureMeasurements) => res.json(moistureMeasurements)) 19 | .catch((err) => res.status(400).json("Error: " + err)); 20 | }); 21 | 22 | // add new moisture measurement 23 | router.route("/add").post((req, res) => { 24 | const username = req.body.username; 25 | const plantName = req.body.plantName; 26 | const moistureReading = req.body.moistureReading; 27 | const date = DateTime.local().minus({ hours: 5 }); 28 | 29 | const newMoistureMeasurement = new MoistureMeasurement({ 30 | username, 31 | plantName, 32 | moistureReading, 33 | date, 34 | }); 35 | 36 | newMoistureMeasurement 37 | .save() 38 | .then(() => res.json("Moisture reading added!")) 39 | .catch((err) => res.status(400).json("Error: " + err)); 40 | }); 41 | 42 | // view specific moisture measurement 43 | router.route("/:id").get((req, res) => { 44 | MoistureMeasurement.findById(req.params.id) 45 | .then((moistureMeasurement) => res.json(moistureMeasurement)) 46 | .catch((err) => res.status(400).json("Error: " + err)); 47 | }); 48 | 49 | // delete a specific moisture measurement 50 | router.route("/:id").delete((req, res) => { 51 | MoistureMeasurement.findByIdAndDelete(req.params.id) 52 | .then(() => res.json("Moisture measurement deleted.")) 53 | .catch((err) => res.status(400).json("Error: " + err)); 54 | }); 55 | 56 | // edit a specific moisture measurement 57 | router.route("/edit/:id").post((req, res) => { 58 | MoistureMeasurement.findById(req.params.id) 59 | .then((moistureMeasurement) => { 60 | moistureMeasurement.plantName = req.body.plantName; 61 | moistureMeasurement.moistureReading = req.body.moistureReading; 62 | moistureMeasurement.date = Date.parse(req.body.date); 63 | moistureMeasurement 64 | .save() 65 | .then(() => res.json("Moisture reading updated!")) 66 | .catch((err) => res.status(400).json("Error: " + err)); 67 | }) 68 | .catch((err) => res.status(400).json("Error: " + err)); 69 | }); 70 | 71 | module.exports = router; 72 | --------------------------------------------------------------------------------