diff --git a/README.md b/README.md index 380b1d8..2c5000b 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,17 @@ # 🌿 Enviro Plus Web Web interface for [Enviro](https://shop.pimoroni.com/products/enviro?variant=31155658489939) and [Enviro+](https://shop.pimoroni.com/products/enviro?variant=31155658457171) sensor board plugged into a Raspberry Pi. -This simple Flask application serves a web page with the current sensor readings and a graph over a specified time period. +This simple webapp serves a page with the current sensor readings in a graph over a specified time period. + +![Screenshot](screenshot-lightTheme.jpg) + +![Screenshot](screenshot-darkTheme.jpg) -![Screenshot](screenshot.jpg) Forked from +⚠️ Enviro readings must not be relied upon for critical applications. + ## 📖 User guide ### Install @@ -28,7 +33,7 @@ The install script enables I2C, SPI, and serial, disables the serial console, an To check that everything is working correctly, go to the enviroplus-python folder and run the all-in-one example: -``` +```console cd examples python all-in-one.py @@ -38,36 +43,37 @@ Tap your finger on the board’s light sensor to cycle through data from differe You can now install the EnviroPlusWeb, from a Terminal window enter: -``` +```console cd git clone https://gitlab.com/idotj/enviroplusweb.git ``` ### Setup + Check at the beginning of the file `enviroplusweb.py` the following lines and choose `True` or `False` depending on your config: - If you have an Enviro board without gas sensor, edit this line - ``` + ```python gas_sensor = False ``` - If you don't have a particulate sensor [PMS5003](https://shop.pimoroni.com/products/pms5003-particulate-matter-sensor-with-cable?variant=29075640352851) attached, edit this line - ``` + ```python particulate_sensor = False ``` - If you prefer to keep the Enviro LCD screen off, edit this line - ``` + ```python lcd_screen = False ``` - If you don't have a fan plugged on GPIO, edit this line - ``` + ```python fan_gpio = False ``` @@ -79,39 +85,51 @@ Find an alternative device/reference to measure the temperature and humidity. Th Maybe you want to run Enviro Plus Web at boot, then just type in the terminal: -``` +```console crontab -e ``` Add a new entry at the very bottom with `@reboot` to specify that you want to run the command at boot, followed by the path where you clone the project and the command. Here you have an example: -``` +```console @reboot sudo python3 /home/EnviroPlusWeb/enviroplusweb.py & ``` +## 🚀 Improve me + +Feel free to add/improve the app and add more features. + +## ⚖️ License + +GNU General Public License v3.0 + ## 💬 FAQ -### Where are my data readings? -Depends on where you run `enviroplusweb.py`. By default your data will be stored in the same place where you have the application, in a JSON format inside a folder called `/data`. -But if you run the app at bootup (for example, using the *crontab*) then your folder `/data` will be at root. +- ### Where are my data readings? -### How can I get my Raspberry Pi IP? -Enter `hostname -I` in a Terminal window on your Raspberry Pi, then you will see the IPv4 and the IPv6. + Depends on where you run `enviroplusweb.py`. By default your data will be stored in the same place where you have the application, in a JSON format inside a folder called `/enviro-data`. + But if you run the app at bootup (for example, using the *crontab*) then your folder `/enviro-data` will be at root. -### My Raspberry Pi is running other services at localhost -You can change the port to avoind any conflict with other applications. In that case edit the file `enviroplusweb.py` and find at the end this line: +- ### How can I get my Raspberry Pi IP? -``` -app.run(debug = False, host = '0.0.0.0', port = 80, use_reloader = False) -``` + Enter `hostname -I` in a Terminal window on your Raspberry Pi, then you will see the IPv4 and the IPv6. -Just change the `port = 80` value for another number (for example 81) and now you can access to your EnviroPlusWeb typing the ip address followed by :81 +- ### My Raspberry Pi is running other services at localhost + You can change the port to avoind any conflict with other applications. In that case edit the file `enviroplusweb.py` and find at the end this line: -## 🚀 Improve me + ```python + app.run(debug = False, host = '0.0.0.0', port = 80, use_reloader = False) + ``` -Feel free to add/improve the app and add more features. + Just change the `port = 80` value for another number (for example 81) and now you can access to your EnviroPlusWeb typing the ip address followed by :81 -## ⚖️ License +- ### I want to run my EnviroPlusWeb under HTTPS -GNU General Public License v3.0 + By default you use HTTP to connect to your Raspberry Pi through your browser, but some browsers will redirect automatically to HTTPS. If you prefer to have your project running under HTTPS here you have a tutorial explaning how to setup Flask with HTTPS: + + +### Other answered questions + +Check the [closed issues](https://gitlab.com/idotj/enviroplusweb/-/issues/?sort=created_date&state=closed&first_page_size=20), you might find your question there. +If nothing matches with your problem, check the [open issues](https://gitlab.com/idotj/enviroplusweb/-/issues/?sort=created_date&state=opened&first_page_size=20) or feel free to create a new one. diff --git a/enviroplusweb.py b/enviroplusweb.py index 52b601e..573ad99 100644 --- a/enviroplusweb.py +++ b/enviroplusweb.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # +# Git repo # Forked from # # EnviroPlusWeb is free software: you can redistribute it and/or modify it under the terms of the @@ -12,9 +13,9 @@ # # If you prefer to keep the Enviro LCD screen off, change the next value to False -lcd_screen = True +lcd_screen = False # If you don't have a fan plugged on GPIO, change the next value to False -fan_gpio = True +fan_gpio = False # If you have an Enviro board without gas sensor, change the next value to False gas_sensor = True # If you don't have a particle sensor PMS5003 attached, change the next value to False @@ -100,13 +101,13 @@ def get_cpu_temperature(): "%", "mBar", "Lux"] - + if gas_sensor: units += [ "kΩ", "kΩ", "kΩ"] - + if particulate_sensor: units += [ "/0.ll", @@ -133,7 +134,6 @@ def display_everything(): draw.text((x, y), message, font = smallfont, fill = rgb) st7735.display(img) - app = Flask(__name__) app.config['TEMPLATES_AUTO_RELOAD'] = True log = logging.getLogger("werkzeug") @@ -166,7 +166,7 @@ def read_data(time): nh3 = round(gases.nh3 / 1000) else: oxi = red = nh3 = 0 - + if particulate_sensor: while True: try: @@ -178,7 +178,7 @@ def read_data(time): raise e pms5003.reset() sleep(30) - + pm100 = particles.pm_per_1l_air(10.0) pm50 = particles.pm_per_1l_air(5.0) - pm100 pm25 = particles.pm_per_1l_air(2.5) - pm100 - pm50 @@ -187,7 +187,7 @@ def read_data(time): pm3 = particles.pm_per_1l_air(0.3) - pm100 - pm50 - pm25 - pm10 - pm5 else: pm100 = pm50 = pm25 = pm10 = pm5 = pm3 = 0 - + record = { 'time' : asctime(localtime(time)), 'temp' : round(temperature,1), @@ -211,7 +211,7 @@ def read_data(time): days = [] def filename(t): - return strftime("data/%Y_%j", localtime(t)) + return strftime("enviro-data/%Y_%j", localtime(t)) def sum_data(data): totals = {"time" : data[0]["time"]} @@ -282,7 +282,6 @@ def readings(): if fan_gpio: arg = request.args["fan"] pwm.ChangeDutyCycle(int(arg)) - # return render_template('readings.html' if particulate_sensor else 'readings_np.html' if gas_sensor else 'readings_ng.html', **record) return record def compress_data(ndays, nsamples): @@ -324,11 +323,11 @@ def read_day(fname): return day if __name__ == '__main__': - if not os.path.isdir('data'): - os.makedirs('data') - files = sorted(os.listdir('data')) + if not os.path.isdir('enviro-data'): + os.makedirs('enviro-data') + files = sorted(os.listdir('enviro-data')) for f in files: - days.append(read_day('data/' + f)) + days.append(read_day('enviro-data/' + f)) background_thread.start() try: app.run(debug = False, host = '0.0.0.0', port = 80, use_reloader = False) diff --git a/screenshot-darkTheme.jpg b/screenshot-darkTheme.jpg new file mode 100644 index 0000000..106545b Binary files /dev/null and b/screenshot-darkTheme.jpg differ diff --git a/screenshot-lightTheme.jpg b/screenshot-lightTheme.jpg new file mode 100644 index 0000000..a0b606d Binary files /dev/null and b/screenshot-lightTheme.jpg differ diff --git a/screenshot.jpg b/screenshot.jpg deleted file mode 100644 index 7d21024..0000000 Binary files a/screenshot.jpg and /dev/null differ diff --git a/static/css/main.css b/static/css/main.css index 638fff5..0a5c764 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -163,10 +163,10 @@ button, /* Header */ .header { - position: fixed; display: flex; width: 100%; height: 3rem; + position: sticky; top: 0; background-color: var(--color-white); box-shadow: 0 0.15rem 0.5rem 0 var(--color-gray); @@ -341,6 +341,7 @@ body[data-hasFanGPIO="False"] .container-btn-fanGpio { background: var(--color-white); } .theme-dark .container-menu-settings.menu-settings-open { + border-left: 1px solid var(--color-gray-darker); background: var(--color-black); } .container-menu-settings > div { @@ -360,9 +361,10 @@ body[data-hasFanGPIO="False"] .container-btn-fanGpio { /* Main container */ .main { display: grid; + height: calc(100% - 6.5rem); grid-template-columns: 19rem 1fr; grid-template-rows: 1fr; - margin: 5rem 2rem 1rem 2rem; + margin: 2.5rem 2rem 1rem 2rem; } .container-readings, .container-graph { diff --git a/static/js/main.js b/static/js/main.js index 6cdb773..442beba 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -102,7 +102,7 @@ var firstLayoutRender = true; var containerCanvas; var canvas; var ctx; -var data; +var dataGraph; var dataReadings; const yScaleSteps = 10; const yLabelHeight = 10; @@ -187,7 +187,7 @@ function getGraph(param) { // Draw the background grid and labels function graph(d) { - data = d; + dataGraph = d; containerCanvas = document.getElementById("container-graph"); canvas = document.getElementById("canvas-graph"); if (firstLayoutRender) { @@ -204,7 +204,7 @@ function graph(d) { ctx.font = "20 pt Verdana"; yScale = canvas.height - yLabelHeight - xLabelHeight; - xScale = (canvas.width - yLabelWidth) / (data.length - 1); + xScale = (canvas.width - yLabelWidth) / (dataGraph.length - 1); ctx.clearRect(0, 0, canvas.width, canvas.height); // Print X axis vertical time lines @@ -227,8 +227,8 @@ function graph(d) { Dec: 11, }; ctx.textAlign = "center"; - for (var i = 0; i < data.length; i++) { - var fields = data[i]["time"].match(/\S+/g); // split is broken! + for (var i = 0; i < dataGraph.length; i++) { + var fields = dataGraph[i]["time"].match(/\S+/g); // split is broken! var t = fields[3].split(":"); var date = months[fields[1]] * 31 + parseInt(fields[2]) - 1; var time = @@ -285,8 +285,8 @@ function graph(d) { var items = particulate_sensor ? items_ngp.concat(items_g).concat(items_p) : gas_sensor - ? items_ngp.concat(items_g) - : items_ngp; + ? items_ngp.concat(items_g) + : items_ngp; for (var item of items) { ctx.strokeStyle = item.colour; plotData(item.name, item.min, item.max); @@ -307,11 +307,11 @@ function plotData(dataSet, min, max) { ctx.beginPath(); ctx.setLineDash([]); y0 = canvas.height - xLabelHeight; - ctx.moveTo(yLabelWidth, y0 - scaley(data[0][dataSet], min, max)); - for (var i = 1; i < data.length; i++) { + ctx.moveTo(yLabelWidth, y0 - scaley(dataGraph[0][dataSet], min, max)); + for (var i = 1; i < dataGraph.length; i++) { ctx.lineTo( yLabelWidth + i * xScale, - y0 - scaley(data[i][dataSet], min, max) + y0 - scaley(dataGraph[i][dataSet], min, max) ); } ctx.stroke(); @@ -323,10 +323,15 @@ function scaley(y, min, max) { } // Update the graph layout (width/height) if window resize -window.addEventListener("resize", function () { +window.addEventListener('resize', function () { + var resizeId; + clearTimeout(resizeId); + resizeId = setTimeout(doneResizing, 500); +}); +function doneResizing() { firstLayoutRender = true; getGraph(true); -}); +} // Control main menu (mobile) const menuMainBtn = document.getElementById("menu-hamburger"); diff --git a/templates/index.html b/templates/index.html index 9b6d1c3..cfb7718 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,244 +1,349 @@ + + + Enviro Plus Web + + + + + + + + + - - - Enviro Plus Web - - - - - - - - - - - - - -
-
-

Enviro Plus Web

-
- -
- -
- -
-
- -
- -
- - - - - -
- -
- - - - - + + +
+
+

Enviro Plus Web

-
- -
- -
+
+
+ +
-
+
+ + + + + +
- -
-
- - - - - - - - - - - - - - - - - - - -
▣ Temperature:°C
▣ Humidity:%
▣ Pressure: mBar
▣ Light: lux
+
+ + + + + +
-
- Gas - - - - - - - - - - - - - - - -
▣ Oxidising: kΩ
▣ Reducing: kΩ
- ▣ NH3: -  kΩ
-
+
+ + +
+ + +
+ -
- Particulate matter + +
+
- - + + - - + + - - + + - - - - - - - - - - + +
▣ >0.3um: / 0.1l▣ Temperature:°C
▣ >0.5um: / 0.1l▣ Humidity:%
▣ >1.0um: / 0.1l▣ Pressure: mBar
▣ >2.5um: / 0.1l
▣ >5.0um: / 0.1l
▣ >10.0um: / 0.1l▣ Light: lux
-
-
- Scale factors - - -
-
- +
+ Gas + + + + + + + + + + + + + + + +
▣ Oxidising: kΩ
▣ Reducing: kΩ
+ ▣ NH3: +  kΩ
+
-
- -
-
+
+ Particulate matter + + + + + + + + + + + + + + + + + + + + + + + + + + + +
▣ >0.3um: / 0.1l
▣ >0.5um: / 0.1l
▣ >1.0um: / 0.1l
▣ >2.5um: / 0.1l
▣ >5.0um: / 0.1l
▣ >10.0um: / 0.1l
+
- - - +
+ Scale factors + + +
+
+ + +
+ +
+ + + +