commit 4e95570fbcd55b1e7d6ad687056fbc94096cc0d4 Author: git Date: Mon Mar 22 21:23:14 2021 +0100 Set new Base version diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8f6f8e1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,79 @@ +# Created by .ignore support plugin (hsz.mobi) +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST +*.manifest +*.spec +pip-log.txt +pip-delete-this-directory.txt +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ +*.mo +*.pot +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal +instance/ +.webassets-cache +.scrapy +docs/_build/ +.pybuilder/ +target/ +.ipynb_checkpoints +profile_default/ +ipython_config.py +__pypackages__/ +celerybeat-schedule +celerybeat.pid +*.sage.py +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +.spyderproject +.spyproject +.ropeproject +/site +.mypy_cache/ +.dmypy.json +dmypy.json +.pyre/ +.pytype/ +cython_debug/ +/src/config.ini +/.idea/ +/venv/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1f286c1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM python:3-alpine + +COPY . /code/ + +RUN pip install -r /code/requirements.txt + +WORKDIR /code/src/ + +CMD ["python","app.py"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..144f28d --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ +# PiSwitch_Reporting +reporting server/API for the [PiSwitch](https://git.dennisvandermeulen.nl/dennis/PiSwitch) + +## Installation Process + +Clone the repository +```shell +git clone https://git.dennisvandermeulen.nl/dennis/PiSwitch_reporting.git +cd PiSwitch_reporting/ +``` +Setup virtual environment & install requirements (skip if you use Docker) +```shell +python3 -m venv venv +source venv/bin/activate +pip3 install -r requirements.txt +``` +Setup config.ini +```shell +nano src/config.ini +``` +setup database config +```ini +[database] +host = +user = +password = +db = +``` +setup apikey for this server +```ini +[setup] +apikey = string +```` +##Docker +use the included Dockerfile +```shell +docker run . -t piswitch/reporting -d -p 5000:5000 +``` +or build the dockerfile for use with [PiSwitch_frontend](https://git.dennisvandermeulen.nl/dennis/PiSwitch_frontend) +```shell +docker build . -t piswitch/reporting +``` + +## Systemd +Use the following script to automatically start the scripts at boot + +````ini +[Unit] +Description=PiSwitch Reporting server +After=multi-user.target + +[Service] +Type=simple +User= +Group= +WorkingDirectory=/path/to/PiSwitch_reporting/src +ExecStart=/path/to/venv/bin/python3 path/to/PiSwitch_reporting/src/server.py + +[Install] +WantedBy=multi-user.target +```` +save as PiSwitch_reporting.service \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..9bfb9fd --- /dev/null +++ b/requirements.txt @@ -0,0 +1,13 @@ +astral==2.2 +click==7.1.2 +configparser==5.0.0 +Flask==1.1.2 +Flask-Cors==3.0.9 +itsdangerous==1.1.0 +Jinja2==2.11.2 +MarkupSafe==1.1.1 +mysql-connector-python==8.0.23 +protobuf==3.15.6 +pytz==2020.1 +six==1.15.0 +Werkzeug==1.0.1 diff --git a/src/app.py b/src/app.py new file mode 100644 index 0000000..3508fa4 --- /dev/null +++ b/src/app.py @@ -0,0 +1,98 @@ +from flask import Flask, request, jsonify +from flask_cors import CORS +import configparser +from helper import storecmd, getcmd, updatestatus, getstatus, getalldata, updatelocation, getdbtime, datelookup +import datetime + +app = Flask(__name__) +CORS(app) + + +def verifyapikey(requestapikey): + if requestapikey == apiKey: + return True + return False + + +@app.route('/cmd', methods=['POST', 'GET']) +def command(): + if verifyapikey(request.values.get('apikey')): + if request.method == 'POST': + storecmd(request.values.get('id'), datetime.datetime.now(), request.values.get('response')) + return jsonify(response='success'), 200 + else: + results = getcmd(request.values.get('id')) + x = 0 + response = {} + for i in results: + response[x] = {'date': i[0], 'command': i[1]} + x = x + 1 + return response, 200 + return jsonify(response='invalid api key'), 401 + + +@app.route('/status', methods=['POST', 'GET']) +def status(): + if verifyapikey(request.values.get('apikey')): + if request.method == 'POST': + updatestatus(request.values.get('id'), datetime.datetime.now(), request.values.get('key'), + request.values.get('value')) + return jsonify(response='success'), 200 + else: + result = getstatus(request.values.get('id'), request.values.get('key')) + return jsonify(response=result[0]), 200 + return jsonify(response='invalid api key'), 401 + + +@app.route('/gettime', methods=['POST']) +def gettime(): + if verifyapikey(request.values.get('apikey')): + result = getdbtime(request.values.get('id'), request.values.get('key')) + return jsonify(response=str(result[0])), 200 + return jsonify(response='invalid api key'), 401 + + +@app.route('/fullstatus', methods=['POST']) +def fullstatus(): + if verifyapikey(request.values.get('apikey')): + result = getalldata(request.values.get('id')) + return {'switch_name': result[1], + 'switch_status': result[3], + 'astral_status': result[4], + 'conn_status': result[5], + 'last_updated': result[2], + 'on_time': str(result[6]), + 'off_time': str(result[7]), + 'loc_name': result[8], + 'loc_region': result[9], + 'loc_lat': result[10], + 'loc_lon': result[11], + 'loc_timezone': result[12], + 'apikey': result[13] + }, 200 + return jsonify(response='invalid api key'), 401 + + +@app.route('/updatelocation', methods=['POST']) +def updateloc(): + if verifyapikey(request.values.get('apikey')): + updatelocation(request.values.get('id'), datetime.datetime.now(), request.values.get('loc_name'), + request.values.get('loc_region'), request.values.get('loc_lat'), request.values.get('loc_lon')) + return jsonify(response='success'), 200 + return jsonify(response='invalid api key'), 401 + + +@app.route('/datelookup', methods=['POST']) +def lookupdate(): + if verifyapikey(request.values.get('apikey')): + result = datelookup(request.values.get('id'), + request.values.get('day'), request.values.get('month'), request.values.get('year')) + return jsonify(response=result), 200 + return jsonify(response='invalid api key'), 401 + + +if __name__ == '__main__': + config = configparser.ConfigParser() + config.read('config.ini') + apiKey = config['setup']['apikey'] + app.run(host='0.0.0.0') diff --git a/src/helper.py b/src/helper.py new file mode 100644 index 0000000..e964ea1 --- /dev/null +++ b/src/helper.py @@ -0,0 +1,93 @@ +import mysql.connector +import configparser +import astral +import astral.sun +import datetime + +config = configparser.ConfigParser() +config.read('config.ini') +host = config['database']['host'] +user = config['database']['user'] +password = config['database']['password'] +db = config['database']['db'] + + +def storecmd(device_id, date_time, command): + dwh = mysql.connector.connect(host=host,user=user,password=password,database=db) + c = dwh.cursor() + c.execute("""INSERT INTO commands(device_id,date_time,command) VALUES(%s,%s,%s)""", (device_id, date_time, command)) + dwh.commit() + c.close() + + +def getcmd(device_id): + dwh = mysql.connector.connect(host=host,user=user,password=password,database=db) + c = dwh.cursor() + c.execute("""SELECT date_time, command FROM commands WHERE device_id = %s ORDER BY date_time DESC LIMIT 10 """, + (device_id,)) + result = c.fetchmany(10) + c.close() + return result + + +def updatestatus(device_id, date_time, key, value): + if key in ['device_id', 'name', 'last_updated', 'switch_status', 'astral_status', 'conn_status', 'on_time', + 'off_time', 'loc_name', 'loc_region', 'loc_lat', 'loc_lon', 'loc_timezone', 'apikey']: + dwh = mysql.connector.connect(host=host,user=user,password=password,database=db) + c = dwh.cursor() + c.execute("""UPDATE status SET last_updated = %s, {} = %s WHERE device_id = %s""".format(key), + (date_time, value, device_id)) + dwh.commit() + c.close() + + +def getstatus(device_id, key): + dwh = mysql.connector.connect(host=host,user=user,password=password,database=db) + c = dwh.cursor() + c.execute("""SELECT {} FROM status WHERE device_id = %s""".format(key), ([device_id])) + result = c.fetchone() + c.close() + return result + + +def getalldata(device_id): + dwh = mysql.connector.connect(host=host,user=user,password=password,database=db) + c = dwh.cursor() + c.execute("""SELECT * FROM status WHERE device_id = %s""", ([device_id])) + result = c.fetchone() + c.close() + return result + + +def updatelocation(device_id, date_time, loc_name, loc_region, loc_lat, loc_lon): + dwh = mysql.connector.connect(host=host,user=user,password=password,database=db) + c = dwh.cursor() + c.execute( + """UPDATE status SET last_updated = %s, loc_name = %s, loc_region = %s, loc_lat = %s, loc_lon = %s WHERE device_id = %s""", + (date_time, loc_name, loc_region, loc_lat, loc_lon, device_id)) + dwh.commit() + c.close() + + +def getdbtime(device_id, key): + if key in ['device_id', 'name', 'last_updated', 'switch_status', 'astral_status', 'conn_status', 'on_time', + 'off_time', 'loc_name', 'loc_region', 'loc_lat', 'loc_lon', 'loc_timezone', 'apikey']: + dwh = mysql.connector.connect(host=host,user=user,password=password,database=db) + c = dwh.cursor() + c.execute("""SELECT {} FROM status WHERE device_id = %s""".format(key), ([device_id])) + result = c.fetchone() + c.close() + return result + + +def datelookup(device_id, day, month, year): + dwh = mysql.connector.connect(host=host,user=user,password=password,database=db) + c = dwh.cursor() + c.execute("""SELECT loc_name, loc_region, loc_lat, loc_lon, loc_timezone FROM status WHERE device_id = %s""", + (device_id,)) + result = c.fetchone() + c.close() + l = astral.LocationInfo(name=result[0], region=result[1], latitude=result[2], longitude=result[3], + timezone=result[4]) + triggerontime = astral.sun.sunset(observer=l.observer, date=datetime.date(day, month, year)) + return triggerontime