diff --git a/.gitignore b/.gitignore index 5d97336..aa759ac 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,7 @@ *log *.venv *certs* -*__pycache__* \ No newline at end of file +*__pycache__* +*.egg* +dist +build \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..99ed105 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,8 @@ +include LICENSE +include README.md +include conf/logger.ini +include src/flaskapp/templates/* +include src/flaskapp/static/css/* +include src/flaskapp/static/js/* +include src/flaskapp/static/img/* +include src/flaskapp/static/fonts/* \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e25ebf0 --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +init: + pip install -r requirements.txt + +test: + nosetests tests \ No newline at end of file diff --git a/README.md b/README.md index ee65b99..530048f 100755 --- a/README.md +++ b/README.md @@ -53,26 +53,26 @@ The server that runs on the Raspberry PI is available on a third [project](https ## Schematic -![Cloud Schematic](./flaskapp/static/img/aws.png) +![Cloud Schematic](src/flaskapp/static/img/aws.png) ## How to run Ensure to have: - Installed python3. -- Installed and configured your AWS CLI (with a role that has permission to access Dynamodb and AWS IoT). -- Created folders: - - /certs - - certificate.pem.crt - - private.pem.key - - rootca.pem - - /log - - Virtual Environment - - ```python3 -m venv .venv``` - - ```source .venv/bin/activate``` +- Installed and configured your AWS CLI (with a role that has permission to access Dynamodb, AWS IoT and S3 Bucket). +- Have an S3 Bucket with the certificates in the following name pattern: + - 'rootca.pem' the Root CA certificate; + - 'certificate.pem.crt' for the IoT Certificate attached to the things; + - 'private.pem.key' for the private key attached to the things. +- Virtual Environment + - ```python3 -m venv .venv``` + - ```source .venv/bin/activate``` + - ```pip3 install SmartGarden``` To run: - Server: - - ```pip3 install -r requirements.txt -t .``` - - ```sudo nohup python3 server.py &``` + - ```pip3 setup.py install``` + - Locally:```sudo nohup smart_garden [-h] [--profile_name PROFILE_NAME] --s3-bucket S3_BUCKET [--aws_region AWS_REGION] --port 5000``` + - Not Locally: ```sudo nohup smart_garden [-h] [--profile_name PROFILE_NAME] --s3-bucket S3_BUCKET [--aws_region AWS_REGION] --port 80``` Useful commands: - Get IP: ```ping raspberrypi.local``` @@ -81,6 +81,12 @@ Useful commands: - Find server process (MAC): ```netstat -vanp tcp | grep 5000``` - Kill process: ```kill [port number]``` +## How to Deploy +- Generete the Distribution file: ```python3 setup.py install bdist_wheel --universal sdist``` +- Upload to Pypi: ```twine upload dist/*``` +- Upload to Pypi Test: ```twine upload --repository-url https://test.pypi.org/legacy/ dist/*``` + + ## References - [Project Reference](https://www.hackster.io/mokxf16/smart-garden-raspberry-pi-arduino-65c7b7) - [Raspberry Server](https://github.com/wenzaca/SmartGardenRaspberry) diff --git a/logger.ini b/conf/logger.ini similarity index 93% rename from logger.ini rename to conf/logger.ini index e9bf039..0ebd6f6 100644 --- a/logger.ini +++ b/conf/logger.ini @@ -15,7 +15,7 @@ handlers = consoleHandler, fileHandler formatter = root class = handlers.RotatingFileHandler maxBytes = 31457280 -args = ('log/SmartGarden.log',) +args = ('./log/SmartGarden.log',) [handler_fileHandler] formatter = root diff --git a/flaskapp/__init__.py b/flaskapp/__init__.py deleted file mode 100644 index 1e899ba..0000000 --- a/flaskapp/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -import os - -from flask import Flask - -app = Flask(__name__) -app.secret_key = os.urandom(12) - -from . import routes diff --git a/requirements.txt b/requirements.txt index 5b87f84..d791672 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -Flask==1.0.2 -boto3==1.9.118 -AWSIoTPythonSDK==1.4.4 -numpy==1.16.2 -flask_wtf==0.14.2 +Flask==1.1.1 +boto3==1.9.220 +AWSIoTPythonSDK==1.4.7 +numpy==1.17.1 +flask_wtf==0.14.2 \ No newline at end of file diff --git a/server.py b/server.py deleted file mode 100755 index 1f10e89..0000000 --- a/server.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python3 - -import os - -if os.path.isdir('./log') is False: - os.mkdir('./log') - -from flaskapp import app - -if __name__ == '__main__': - app.run(host='127.0.0.1', port=80, use_reloader=False, debug=False) diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..ebe3dac --- /dev/null +++ b/setup.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- + +import os + +from setuptools import setup + +long_description="Smart Garden - IoT Project" \ + "" \ + "" \ + "The smart garden monitors the temperature, humidity, light levels and soil moisture of the plant. It has an automated system that waters the plant when the soil moisture is below an specific value and switches on the fan when the air temperature or humidity are below or above an specific value. This maintains an ideal and consistent soil condition for the plant, and makes it convenient for those who tend to forget to water their plants regularly." \ + "" \ + "We will be using a Raspberry Pi to receive data from the sensors and control the different actuators. The surrounding temperature, air humidity and brightness and soil moisture values will be recorded. These values will then be displayed on a web page, which allow users to know the environmental conditions of the plants when they check on them." \ + "" \ + "When the soil moisture level goes below the user input, and the automatic button is tuned on, the water pump will start to run and pump water into the soil automatically. This is very convenient for users as they do not need to water their plants every time but instead let the system water their plants automatically based on the moisture level of the soil." \ + "" \ + "As for the fans, when the temperature is above the specific or the air humidity is below the specified, the fans will turn on to allow the exchange of air with the outside environment. Mind that the system is inside of a Hydroponic tent." \ + "" \ + "The Light will be turned on constantly based on time (3, 6 or 9 hours) as manual input on the own light timer." \ + "" \ + "The temperature, humidity, light levels and soil moisture values will also be published to DynamoDB. Through a server (Raspberry Pi), the data will be displayed onto a flask web page where it shows real-time data coming from the sensors. This will allow users to view the real-time environmental conditions of the plants on the go (the latest 15 records through a graph)." \ + "" \ + "The web page will also allow users to control the water pump and fans whenever the user decide, based on the automatically or manually input. The web page also counts with authorization based on username and password stored in a Cognito UserPool." \ + "" \ + "The user can also change the inputs values for turning on the fans and the water pump on the web page. By choosing Setting, the user can change the values of temperature, air humidity and moisture." \ + "" \ + "The user is also able to control the system using Alexa assistant. Check the SmartGarden CodeStar [project](https://github.com/wenzaca/SmartGardenAlexa) for more information." \ + "" \ + "The server that runs on the Raspberry PI is available on a third [project](https://github.com/wenzaca/SmartGardenRaspberry) that the user can download and run it on the Raspberry PI. For how to setup the hardware of this project, ensure to check the Raspberry project." + + +with open('LICENSE') as f: + license = f.read() + +setup( + name="SmartGarden", + version="1.0.1", + description="Smart Garden Webserver used to control an Raspberry PI, developed in Python with JS, Jquery and Ajax.", + author='Wendler Zacariotto', + author_email='wenzaca@gmail.com', + url='https://github.com/wenzaca/SmartGarden', + long_description=long_description, + license=license, + keywords=['SmartGarden', 'aws', 'Raspberry'], + + # declare your packages + packages=["src", "src.flaskapp", "src.flaskapp.static.css", "src.flaskapp.templates", "src.flaskapp.static.js", + "src.flaskapp.static.img", "src.flaskapp.static.fonts", "conf"], + + # include data files + include_package_data=True, + exclude_package_data={'': ['.DS_Store']}, + + # requirements + install_requires=['Flask>1.0', 'boto3>1.9.100', 'AWSIoTPythonSDK>1.4.0', 'numpy>1.17.0', 'flask_wtf>0.14.0'], + python_requires='>=3.6.0', + + # run + test_suite="test", + entry_points={'console_scripts': [ + 'smart_garden = src.command_line:main' + ] + } + + +) diff --git a/aws_publish_raspberry_server.py b/src/aws_publish_raspberry_server.py similarity index 93% rename from aws_publish_raspberry_server.py rename to src/aws_publish_raspberry_server.py index 7699d98..4e43c96 100644 --- a/aws_publish_raspberry_server.py +++ b/src/aws_publish_raspberry_server.py @@ -3,7 +3,7 @@ from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient -import log_util +from src import log_util log_util.log_info(__name__, '#################################################################################') log_util.log_info(__name__, '############################# INITIALIZING SERVER #############################') @@ -15,9 +15,9 @@ topic_max_data = "smartgarden/maxdata" host = "arkau3u0cw2s4-ats.iot.eu-west-1.amazonaws.com" -rootCAPath = "certs/rootca.pem" -certificatePath = "certs/certificate.pem.crt" -privateKeyPath = "certs/private.pem.key" +rootCAPath = "./certs/rootca.pem" +certificatePath = "./certs/certificate.pem.crt" +privateKeyPath = "./certs/private.pem.key" # Stablishing MQTT Connection my_rpi = AWSIoTMQTTClient("Raspbery_Core") diff --git a/src/command_line.py b/src/command_line.py new file mode 100644 index 0000000..01a9d67 --- /dev/null +++ b/src/command_line.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 + +import os +import argparse +import boto3 + +if os.path.isdir('log') is False: + os.mkdir('log') + + +def download_certificate(profile_name, s3_bucket, region): + session = boto3.session.Session(profile_name=profile_name) + s3 = session.client('s3', region_name=region) + if os.path.isdir('./certs') is False: + os.mkdir('./certs') + s3.download_file(s3_bucket, 'rootca.pem', './certs/rootca.pem') + s3.download_file(s3_bucket, 'certificate.pem.crt', './certs/certificate.pem.crt') + s3.download_file(s3_bucket, 'private.pem.key', './certs/private.pem.key') + + +def main(): + parser = argparse.ArgumentParser("smart_garden", description="WebServer stater point for Smart Garden") + parser.add_argument("--s3-bucket", '-b', help="The S3 Bucket where the certificates were uploaded", type=str, + required=True, metavar='bucket/name/and/folder') + parser.add_argument("--profile_name", '-u', help="The Credential User", type=str, required=False, + default='default', metavar='default') + parser.add_argument("--aws_region", '-r', help="The Bucket and the AWS IoT Region", type=str, required=False, + default='us-east-1', metavar='us-east-1') + parser.add_argument("--port", '-p', help="The port that you want this server to run", type=int, required=False, + default=80, metavar='80') + args = parser.parse_args() + download_certificate(args.profile_name, args.s3_bucket, args.aws_region) + from src.flaskapp import app + + app.run(host='127.0.0.1', port=args.port, use_reloader=False, debug=False) + + + diff --git a/src/flaskapp/__init__.py b/src/flaskapp/__init__.py new file mode 100644 index 0000000..929b6b7 --- /dev/null +++ b/src/flaskapp/__init__.py @@ -0,0 +1,12 @@ +import os + +from flask import Flask + +this_dir, this_filename = os.path.split(__file__) +template_path = os.path.join(os.path.dirname(this_dir), "flaskapp", "templates") +static_path = os.path.join(os.path.dirname(this_dir), "flaskapp", "static") + +app = Flask("SmartGarden Webserver", template_folder=template_path, static_folder=static_path, static_url_path="/static") +app.secret_key = os.urandom(12) + +from . import routes diff --git a/flaskapp/forms.py b/src/flaskapp/forms.py similarity index 100% rename from flaskapp/forms.py rename to src/flaskapp/forms.py diff --git a/flaskapp/routes.py b/src/flaskapp/routes.py similarity index 97% rename from flaskapp/routes.py rename to src/flaskapp/routes.py index 97fdcbc..d21ea34 100644 --- a/flaskapp/routes.py +++ b/src/flaskapp/routes.py @@ -3,11 +3,9 @@ import boto3 from flask import render_template, url_for, redirect, request, jsonify, session -import jsonconverter as jsonc -import log_util -import repository_dynamo -from flaskapp import app -from flaskapp.forms import LoginForm +from src import jsonconverter as jsonc, repository_dynamo, log_util +from src.flaskapp import app +from src.flaskapp.forms import LoginForm cognito = client = boto3.client('cognito-idp') diff --git a/flaskapp/static/css/animate.min.css b/src/flaskapp/static/css/animate.min.css similarity index 100% rename from flaskapp/static/css/animate.min.css rename to src/flaskapp/static/css/animate.min.css diff --git a/flaskapp/static/css/bootstrap.min.css b/src/flaskapp/static/css/bootstrap.min.css similarity index 100% rename from flaskapp/static/css/bootstrap.min.css rename to src/flaskapp/static/css/bootstrap.min.css diff --git a/flaskapp/static/css/main.css b/src/flaskapp/static/css/main.css similarity index 100% rename from flaskapp/static/css/main.css rename to src/flaskapp/static/css/main.css diff --git a/flaskapp/static/css/paper-dashboard.css b/src/flaskapp/static/css/paper-dashboard.css similarity index 100% rename from flaskapp/static/css/paper-dashboard.css rename to src/flaskapp/static/css/paper-dashboard.css diff --git a/flaskapp/static/css/themify-icons.css b/src/flaskapp/static/css/themify-icons.css similarity index 100% rename from flaskapp/static/css/themify-icons.css rename to src/flaskapp/static/css/themify-icons.css diff --git a/flaskapp/static/fonts/themify.eot b/src/flaskapp/static/fonts/themify.eot similarity index 100% rename from flaskapp/static/fonts/themify.eot rename to src/flaskapp/static/fonts/themify.eot diff --git a/flaskapp/static/fonts/themify.svg b/src/flaskapp/static/fonts/themify.svg similarity index 100% rename from flaskapp/static/fonts/themify.svg rename to src/flaskapp/static/fonts/themify.svg diff --git a/flaskapp/static/fonts/themify.ttf b/src/flaskapp/static/fonts/themify.ttf similarity index 100% rename from flaskapp/static/fonts/themify.ttf rename to src/flaskapp/static/fonts/themify.ttf diff --git a/flaskapp/static/fonts/themify.woff b/src/flaskapp/static/fonts/themify.woff similarity index 100% rename from flaskapp/static/fonts/themify.woff rename to src/flaskapp/static/fonts/themify.woff diff --git a/flaskapp/static/img/aws.png b/src/flaskapp/static/img/aws.png similarity index 100% rename from flaskapp/static/img/aws.png rename to src/flaskapp/static/img/aws.png diff --git a/flaskapp/static/img/logo.png b/src/flaskapp/static/img/logo.png similarity index 100% rename from flaskapp/static/img/logo.png rename to src/flaskapp/static/img/logo.png diff --git a/flaskapp/static/js/bootstrap-checkbox-radio.js b/src/flaskapp/static/js/bootstrap-checkbox-radio.js similarity index 100% rename from flaskapp/static/js/bootstrap-checkbox-radio.js rename to src/flaskapp/static/js/bootstrap-checkbox-radio.js diff --git a/flaskapp/static/js/bootstrap.min.js b/src/flaskapp/static/js/bootstrap.min.js similarity index 100% rename from flaskapp/static/js/bootstrap.min.js rename to src/flaskapp/static/js/bootstrap.min.js diff --git a/flaskapp/static/js/chartist.min.js b/src/flaskapp/static/js/chartist.min.js similarity index 100% rename from flaskapp/static/js/chartist.min.js rename to src/flaskapp/static/js/chartist.min.js diff --git a/flaskapp/static/js/jquery.min.js b/src/flaskapp/static/js/jquery.min.js similarity index 100% rename from flaskapp/static/js/jquery.min.js rename to src/flaskapp/static/js/jquery.min.js diff --git a/flaskapp/static/js/main.js b/src/flaskapp/static/js/main.js similarity index 99% rename from flaskapp/static/js/main.js rename to src/flaskapp/static/js/main.js index 0de519d..eeaacb6 100644 --- a/flaskapp/static/js/main.js +++ b/src/flaskapp/static/js/main.js @@ -203,7 +203,7 @@ $(document).ready(function () { getData(); getStatus(); getSettings(); - + getChartData(); setInterval(function () { getData(); diff --git a/flaskapp/static/js/paper-dashboard.js b/src/flaskapp/static/js/paper-dashboard.js similarity index 100% rename from flaskapp/static/js/paper-dashboard.js rename to src/flaskapp/static/js/paper-dashboard.js diff --git a/flaskapp/templates/dashboard.html b/src/flaskapp/templates/dashboard.html similarity index 100% rename from flaskapp/templates/dashboard.html rename to src/flaskapp/templates/dashboard.html diff --git a/flaskapp/templates/graph.html b/src/flaskapp/templates/graph.html similarity index 100% rename from flaskapp/templates/graph.html rename to src/flaskapp/templates/graph.html diff --git a/flaskapp/templates/login.html b/src/flaskapp/templates/login.html similarity index 100% rename from flaskapp/templates/login.html rename to src/flaskapp/templates/login.html diff --git a/flaskapp/templates/navbar.html b/src/flaskapp/templates/navbar.html similarity index 100% rename from flaskapp/templates/navbar.html rename to src/flaskapp/templates/navbar.html diff --git a/flaskapp/templates/setting.html b/src/flaskapp/templates/setting.html similarity index 100% rename from flaskapp/templates/setting.html rename to src/flaskapp/templates/setting.html diff --git a/flaskapp/templates/template.html b/src/flaskapp/templates/template.html similarity index 100% rename from flaskapp/templates/template.html rename to src/flaskapp/templates/template.html diff --git a/jsonconverter.py b/src/jsonconverter.py similarity index 100% rename from jsonconverter.py rename to src/jsonconverter.py diff --git a/log_util.py b/src/log_util.py similarity index 62% rename from log_util.py rename to src/log_util.py index 78f126e..e6817c3 100644 --- a/log_util.py +++ b/src/log_util.py @@ -1,8 +1,12 @@ +import os import logging import logging.config -logging.config.fileConfig(fname='logger.ini', disable_existing_loggers=False) -logging.FileHandler('log/SmartGarden.log') +this_dir, this_filename = os.path.split(__file__) +DATA_PATH = os.path.join(os.path.dirname(this_dir), "conf", "logger.ini") + +logging.config.fileConfig(fname=DATA_PATH, disable_existing_loggers=False) +logging.FileHandler('./log/SmartGarden.log') def log_info(name, log): diff --git a/publish_fake_date.py b/src/publish_fake_date.py similarity index 100% rename from publish_fake_date.py rename to src/publish_fake_date.py diff --git a/repository_dynamo.py b/src/repository_dynamo.py similarity index 98% rename from repository_dynamo.py rename to src/repository_dynamo.py index 36b5e20..0290850 100755 --- a/repository_dynamo.py +++ b/src/repository_dynamo.py @@ -2,8 +2,7 @@ import boto3 -import aws_publish_raspberry_server as core -import log_util +from src import aws_publish_raspberry_server as core, log_util def post_max_data(data): diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..a8bf56f --- /dev/null +++ b/test/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python3 + +print("Create tests") \ No newline at end of file