#!/usr/bin/env python3 # imports import sqlite3 from flask import Flask, request, session, g, redirect, url_for, abort, render_template, flash, json, Response from contextlib import closing from datetime import datetime, timedelta from dateutil import parser from io import BytesIO from matplotlib.figure import Figure import matplotlib.dates as mdates # configuration DATABASE = 'sensors.db' DEBUG = True SECRET_KEY = 'development_key' # app creation app = Flask(__name__) app.config.from_object(__name__) # To read from settings file, set SENSOR_SETTINGS in envvar #app.config.from_envvar('SENSOR_SETTINGS', slient=True) # initialize database def init_db(): with closing(connect_db()) as db: #with app.open_resource('schema.sql', mode='r') as f: # db.cursor().executescript(f.read()) db.commit() # connect to database def connect_db(): return sqlite3.connect(app.config['DATABASE']) # pull data between two dates # startdate is for some date farther in the past than enddate def dates_between(startdate, enddate, table, deviceId): if table is 'weather': subset = g.db.execute('SELECT date, temperature, humidity, pressure FROM weather WHERE date BETWEEN ? AND ?', [startdate, enddate]) else: subset = g.db.execute('SELECT date, temperature, humidity, pressure, battery FROM house WHERE (date BETWEEN ? AND ?) AND deviceId = ?', [startdate, enddate, deviceId]) return subset def getCurrent(requestArgs): # the args should be formatted YYYYMMDDHHMMSS start = requestArgs.get('start') end = requestArgs.get('end') table = requestArgs.get('type') deviceId = request.args.get('deviceId') # Sets default dates of information to be fetched, starttime will default to one day behind endtime, which defaults to now if end is None: endtime = datetime.now() else: endtime = datetime.strptime(end, "%Y%m%d%H%M%S") if start is None: starttime = endtime - timedelta(days=1) else: starttime = datetime.strptime(start, "%Y%m%d%H%M%S") if table is None: table = 'house' if deviceId is None: deviceId = 1 return dates_between(starttime, endtime, table, deviceId) # handle requests @app.before_request def before_request(): g.db = connect_db() @app.teardown_request def teardown_request(exception): db = getattr(g, 'bd', None) if db is not None: db.close() # viewing database @app.route('/') def show_entries(): enddate = datetime.now() startdate = enddate - timedelta(days=1) deviceId = 1 cur = g.db.execute('SELECT date, temperature, humidity, pressure FROM house WHERE (date BETWEEN ? AND ?) AND deviceId = ? ORDER BY id desc ', [startdate, enddate, deviceId]) entries = [dict(date=row[0], temperature=row[1], humidity=row[2], pressure=row[3]) for row in cur.fetchall()] temp = "{:.1f}".format(entries[0].get("temperature") * 1.8 + 32) humid = "{:.1f}".format(entries[0].get("humidity")) current = dict(temperature=temp, humidity=humid) return render_template('show_entries.html', log=entries, current=current, devId=1) @app.route('/bedroom') def bedroom_entries(): enddate = datetime.now() startdate = enddate - timedelta(days=1) deviceId = 1 cur = g.db.execute('SELECT date, temperature, humidity, pressure FROM house WHERE (date BETWEEN ? AND ?) AND deviceId = ? ORDER BY id desc ', [startdate, enddate, deviceId]) entries = [dict(date=row[0], temperature=row[1], humidity=row[2], pressure=row[3]) for row in cur.fetchall()] temp = "{:.1f}".format(entries[0].get("temperature") * 1.8 + 32) humid = "{:.1f}".format(entries[0].get("humidity")) current = dict(temperature=temp, humidity=humid) return render_template('show_entries.html', log=entries, current=current, devId=1) @app.route('/mainfloor') def mainfloor_entries(): enddate = datetime.now() startdate = enddate - timedelta(days=1) deviceId = 2 cur = g.db.execute('SELECT date, temperature, humidity, pressure FROM house WHERE (date BETWEEN ? AND ?) AND deviceId = ? ORDER BY id desc ', [startdate, enddate, deviceId]) entries = [dict(date=row[0], temperature=row[1], humidity=row[2], pressure=row[3]) for row in cur.fetchall()] temp = "{:.1f}".format(entries[0].get("temperature") * 1.8 + 32) humid = "{:.1f}".format(entries[0].get("humidity")) current = dict(temperature=temp, humidity=humid) return render_template('show_entries.html', log=entries, current=current, devId=2) @app.route('/interactive') def interactive(): return render_template('interactive.html') # drawing graphs @app.route('/tgraph.png', methods=['GET']) def draw_t_graph(): cur = getCurrent(request.args) data = cur.fetchall() dates = [] tempsC = [] tempsF = [] for row in data: dates.append(parser.parse(row[0])) tempsC.append(row[1]) tempsF.append(row[1] * 1.8 + 32) # Generate the figure **without using pyplot**. fig = Figure() ax = fig.subplots() bx = ax.twinx() locator = mdates.AutoDateLocator(minticks=3, maxticks=7) formatter = mdates.ConciseDateFormatter(locator) ax.xaxis.set_major_locator(locator) ax.xaxis.set_major_formatter(formatter) bx.xaxis.set_major_locator(locator) bx.xaxis.set_major_formatter(formatter) ax.plot(dates, tempsC, color="C1") ax.set_xlabel("Timestamps") ax.set_ylabel("Celsius") ax.set_title("Temperature") bx.plot(dates, tempsF, visible=False) bx.set_ylabel("Fahrenheit") # Save it to a temporary buffer. buf = BytesIO() fig.savefig(buf, format="png") return Response(buf.getvalue(), mimetype='image/png') @app.route('/hgraph.png', methods=['GET']) def draw_h_graph(): data = getCurrent(request.args) dates = [] humidities = [] for row in data: dates.append(parser.parse(row[0])) humidities.append(row[2]) # Generate the figure **without using pyplot**. fig = Figure() ax = fig.subplots() locator = mdates.AutoDateLocator(minticks=3, maxticks=7) formatter = mdates.ConciseDateFormatter(locator) ax.xaxis.set_major_locator(locator) ax.xaxis.set_major_formatter(formatter) ax.plot(dates, humidities, color="C0") ax.set_xlabel("Timestamps") ax.set_ylabel("Percent") ax.set_title("Relative Humidity") # Save it to a temporary buffer. buf = BytesIO() fig.savefig(buf, format="png") return Response(buf.getvalue(), mimetype='image/png') @app.route('/htgraph.png', methods=['GET']) def draw_ht_graph(): cur = getCurrent(request.args) data = cur.fetchall() dates = [] humidities = [] #tempsC = [] tempsF = [] for row in data: dates.append(parser.parse(row[0])) # tempsC.append(row[1]) tempsF.append(row[1] * 1.8 + 32) humidities.append(row[2]) fig = Figure() #fig.subplots_adjust(right=0.75) ax = fig.subplots() bx = ax.twinx() #cx = ax.twinx() #cx.spines["right"].set_position(("axes", 1.2)) #cx.set_frame_on(True) #cx.patch.set_visible(False) #for sp in cx.spines.values(): # sp.set_visible(False) #cx.spines["right"].set_visible(True) locator = mdates.AutoDateLocator(minticks=3, maxticks=7) formatter = mdates.ConciseDateFormatter(locator) ax.xaxis.set_major_locator(locator) ax.xaxis.set_major_formatter(formatter) bx.xaxis.set_major_locator(locator) bx.xaxis.set_major_formatter(formatter) #cx.xaxis.set_major_locator(locator) #cx.xaxis.set_major_formatter(formatter) hline, = ax.plot(dates, humidities, color="C0", label="Relative Humidity") ax.set_xlabel("Timestamps") ax.set_ylabel("Percent") ax.set_title("Humidity and Temperature") tline, = bx.plot(dates, tempsF, color="C1", label="Temperature") bx.set_ylabel("Fahrenheit") #cx.plot(dates, tempsC, visible=False) #cx.set_ylabel("Celsius") lines = [hline, tline] ax.legend(lines, [l.get_label() for l in lines]) buf = BytesIO() fig.savefig(buf, format="png") return Response(buf.getvalue(), mimetype='image/png') @app.route('/pgraph.png', methods=['GET']) def draw_p_graph(): data = getCurrent(request.args) dates = [] pressures = [] for row in data: dates.append(parser.parse(row[0])) pressures.append(row[3]) # Generate the figure **without using pyplot**. fig = Figure() ax = fig.subplots() locator = mdates.AutoDateLocator(minticks=3, maxticks=7) formatter = mdates.ConciseDateFormatter(locator) ax.xaxis.set_major_locator(locator) ax.xaxis.set_major_formatter(formatter) ax.plot(dates, pressures, color="C2") ax.set_xlabel("Timestamps") ax.set_ylabel("hPa") ax.set_title("Pressure") # Save it to a temporary buffer. buf = BytesIO() fig.savefig(buf, format="png") return Response(buf.getvalue(), mimetype='image/png') @app.route('/bgraph.png', methods=['GET']) def draw_b_graph(): data = getCurrent(request.args) dates = [] batteries = [] for row in data: dates.append(parser.parse(row[0])) batteries.append(row[4]) # Generate the figure **without using pyplot**. fig = Figure() ax = fig.subplots() locator = mdates.AutoDateLocator(minticks=3, maxticks=7) formatter = mdates.ConciseDateFormatter(locator) ax.xaxis.set_major_locator(locator) ax.xaxis.set_major_formatter(formatter) ax.plot(dates, batteries, color="C3") ax.set_xlabel("Timestamps") ax.set_ylabel("Volts") ax.set_title("Battery Voltage") # Save it to a temporary buffer. buf = BytesIO() fig.savefig(buf, format="png") return Response(buf.getvalue(), mimetype='image/png') ## Database Retrieval Route # # Sends the information in the database in JSON format @app.route('/get_database') def get_database(): entries = getCurrent(request.args) empList = [] for emp in entries: empDict = { 'date': emp[0], 'temperature': emp[1], 'humidity': emp[2], 'pressure': emp[3] } empList.append(empDict) response = app.response_class( response=json.dumps(empList), status=200, mimetype='application/json' ) return response # adding entries to database @app.route('/data', methods=['POST', 'GET']) def add_data(): mkey = request.args.get('key') if mkey != SECRET_KEY: abort(401) temp = request.args.get('temp') humidity = request.args.get('humidity') pressure = request.args.get('pressure') now = datetime.now() g.db.execute('INSERT INTO weather (date, temperature, humidity, pressure) VALUES (?, ?, ?, ?)', \ [now, temp, humidity, pressure]) g.db.commit() return render_template('show_entries.html'), 200 @app.route('/json_data', methods=['POST']) def add_json_data(): mkey = request.args.get('key') if mkey != SECRET_KEY: abort(401) # Validate the request body contains JSON if request.is_json: # Parse the JSON into a Python dictionary req = request.get_json() # Print the dictionary print(req) try: temp = req['temperature'] humidity = req['humidity'] pressure = req['pressure'] battery = req['battery'] deviceId = req['deviceId'] now = datetime.now() g.db.execute('INSERT INTO house (date, temperature, humidity, pressure, deviceId, battery) VALUES (?, ?, ?, ?, ?, ?)', [now, temp, humidity, pressure, deviceId, battery]) g.db.commit() except: print("bad json") # Return a string along with an HTTP status code return "JSON received!", 200 else: # The request body wasn't JSON so return a 400 HTTP status code return "Request was not JSON", 400 if __name__ == '__main__': app.run(host='0.0.0.0')