diff --git a/app.py b/app.py index c05cbb7..dcea74f 100755 --- a/app.py +++ b/app.py @@ -6,12 +6,17 @@ from time import sleep import time import requests import json +import paho.mqtt.client as mqtt # global last_indoor_data global indoor_server_ip global indoor_server_password global outdoor_api_url +global outdoor_data global location +global mqtt_enabled +global mqtt_client +global base_topic # Load the config file "config.json" config = json.loads(open("config.json", "r").read()) @@ -19,93 +24,44 @@ indoor_server_ip = config["indoor_server_ip"] indoor_server_password = config["indoor_server_password"] outdoor_api_url = config["outdoor_api_url"] location = config["location"] +mqtt_enabled = config["mqtt_enabled"] -# Assume that the indoor unit is offline -# The get_indoor_data() function will update this variable -last_indoor_data = { - "offline": True -} +# Initialize Variables +outdoor_data = {} -# def get_indoor_data() -> list: -# global indoor_server_ip -# global indoor_server_password -# # SMB server details -# server_name = indoor_server_ip -# share_name = "airvisual" -# username = "airvisual" -# password = indoor_server_password - -# # File details, The file is a text file with name: -# # _AirVisual_values.txt -# # Get the prefix of the file name -# prefix = time.strftime("%Y%m", time.localtime()) -# file_path = prefix + "_AirVisual_values.txt" +def mqtt_reconnect(): + global mqtt_client + global mqtt_server_ip + global mqtt_server_port + global mqtt_username + global mqtt_password + global mqtt_client_id + global base_topic + if not mqtt_client.is_connected(): + # Connect to the MQTT server + mqtt_client.connect(mqtt_server_ip, mqtt_server_port) + # Start the MQTT client + mqtt_client.loop_start() + if mqtt_client.is_connected(): + print("Connected to MQTT server!") + else: + print("Failed to connect to MQTT server!") + return False + return True -# # Connect to the SMB server -# conn = SMBConnection(username, password, "", "") -# conn.connect(server_name, 139) +if mqtt_enabled: + mqtt_server_ip = config["mqtt_server_ip"] + mqtt_server_port = config["mqtt_server_port"] + mqtt_username = config["mqtt_username"] + mqtt_password = config["mqtt_password"] + mqtt_client_id = config["mqtt_client_id"] + base_topic = config["base_topic"] -# # Read the file contents -# file_obj = open(file_path, "wb") -# conn.retrieveFile(share_name, file_path, file_obj) -# conn.close() - -# # Open the local cached file -# file_obj = open(file_path, "r") - -# # The first line of the file contains the header -# # The header contains the column names separated by a semicolon (;) -# # The rest of the file contains the data separated by a semicolon (;) -# # Extract the column names and the data from the file -# file_obj.seek(0) -# header = file_obj.readline().strip().split(";") -# data = file_obj.readlines() -# # Split all the data into a list of lists -# data = [row.strip().split(";") for row in data] -# file_obj.close() - - -# # Remap the header names -# headers_map = { -# "PM2_5(ug/m3)": "pm25", -# "PM10(ug/m3)": "pm10", -# "PM1(ug/m3)": "pm1", -# "CO2(ppm)": "co2", -# "AQI(US)": "aqi", -# "Temperature(C)": "temperature", -# "Humidity(%RH)": "humidity", -# "Timestamp": "time" -# } - -# # Remove rows with header names that are not in the header map -# # First, get the indices of the header names that are in the header map -# headers_indices = [] -# for index, name in enumerate(header): -# if name in headers_map: -# headers_indices.append(index) - -# # Construct the new header with the header names that are in the header map -# header = [header[index] for index in headers_indices] - -# # Construct the new data with only the columns indicated by the header indices -# data = [[row[index] for index in headers_indices] for row in data] - -# # Remap the header names -# headers = [headers_map[name] for name in header] - -# # Convert unix timestamp to human readable time -# for row in data: -# row[headers.index("time")] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(row[headers.index("time")]))) - -# # Create a list of dictionaries representing the data -# # Each dictionary represents a row of data -# data_list = [] -# for row in data: -# data_dict = {} -# for header in headers: -# data_dict[header] = row[headers.index(header)] -# data_list.append(data_dict) -# return data_list + # Create an MQTT client + mqtt_client = mqtt.Client(client_id=mqtt_client_id) + # Set the username and password + mqtt_client.username_pw_set(mqtt_username, mqtt_password) + mqtt_reconnect() def get_outdoor_data_current() -> dict: # Fetch the data from the AirVisual API @@ -142,6 +98,7 @@ def get_outdoor_data_current() -> dict: response = requests.get(url) try: print("Fetching data from API!" ) + print(response) data = response.json() # Create a dictionary of the data data = { @@ -167,7 +124,8 @@ def get_outdoor_data_current() -> dict: # Remove the last_updated key # TODO store the data in a database return data - except: + except Exception as e: + print(e) # Oops, we got rate limited # Return the cached data print("Rate limited!") @@ -196,7 +154,7 @@ app = Flask(__name__) # Refresh the indoor data every 30 seconds def refresh_data(): while True: - + global outdoor_data # print("Fetching indoor data!") # indoor_data = get_indoor_data() # global last_indoor_data @@ -204,9 +162,17 @@ def refresh_data(): # last_indoor_data = indoor_data[-1] # Fetch the outdoor data - print("Fetching outdoor data!") + print("Fetching outdoor data!") outdoor_data = get_outdoor_data_current() - + # Reconnect the MQTT client + mqtt_reconnect() + mqtt_client.publish(base_topic+"/temperature", outdoor_data["temperature"], retain=True) + mqtt_client.publish(base_topic+"/humidity", outdoor_data["humidity"], retain=True) + mqtt_client.publish(base_topic+"/pressure", outdoor_data["pressure"], retain=True) + mqtt_client.publish(base_topic+"/pm25", outdoor_data["pm25"], retain=True) + mqtt_client.publish(base_topic+"/pm10", outdoor_data["pm10"], retain=True) + mqtt_client.publish(base_topic+"/pm1", outdoor_data["pm1"], retain=True) + mqtt_client.publish(base_topic+"/aqi", outdoor_data["aqi"], retain=True) sleep(30) # Start the thread to refresh the data diff --git a/nginx.json b/nginx.json index 8ee4ce4..2b3d3f7 100644 --- a/nginx.json +++ b/nginx.json @@ -1,6 +1,6 @@ { "listeners": { - "*:80": { + "*:8080": { "pass": "routes" } }, @@ -40,7 +40,7 @@ ], "applications": { "iqair-apiserver-satitm": { - "type": "python 3.10", + "type": "python 3.8", "path": "/app/iqair-apiserver", "home": "/app/venv", "module": "app", @@ -48,7 +48,7 @@ "working_directory": "/app/env-satitm" }, "iqair-apiserver-satite": { - "type": "python 3.10", + "type": "python 3.8", "path": "/app/iqair-apiserver", "home": "/app/venv", "module": "app", @@ -57,4 +57,4 @@ } } -} \ No newline at end of file +} diff --git a/static/card.html b/static/card.html index 9ca394e..e7b245a 100644 --- a/static/card.html +++ b/static/card.html @@ -52,7 +52,7 @@ .top_element { display: grid; grid-template-columns: 1fr 2fr 2fr; - background-color: #97FF55; + background-color: #767676; height: 190px; } @@ -104,7 +104,7 @@ justify-content: center; justify-items: center; align-items: center; - background-color: #7bdb3f; + background-color: #474747; font-size: 100px; color: #fff; @@ -161,8 +161,8 @@

Loading . . .

- -

Healthy

+ +

Loading

N/A

@@ -173,7 +173,7 @@

N/A

-

PM2.5 (μg/m3)

+

PM2.5 (μg/m³)

@@ -216,15 +216,21 @@ console.log(data); // Set Title Location String - $("#title").text(data.location); + // The location is the location key in the JSON response + // It must also be appended with the timestamp + var update_time = data.time_outdoor + // Data is in format YYYY-MM-DD hh:mm:ss + // We only want hh:mm + var update_time = update_time.substring(11, 16); + $("#title").text(data.location+" @ "+update_time); // Update outdoor AQI and PM2.5 values $("#outdoor-aqi").text(data.aqi_outdoor); - $("#outdoor-pm25").text(data.pm25_outdoor); + $("#outdoor-pm25").text(data.pm25_outdoor.toFixed(1)); // Update outdoor temperature and humidity values - $("#outdoor-temp").text("Temperature: " + data.temperature_outdoor + "°C"); - $("#outdoor-humid").text("Humidity: " + data.humidity_outdoor + "%"); + $("#outdoor-temp").text("Temperature: " + data.temperature_outdoor.toFixed(1) + "°C"); + $("#outdoor-humid").text("Humidity: " + data.humidity_outdoor.toFixed(1) + "%"); // Is outdoor AQI healthy? // If AQI is less than 50, it is healthy diff --git a/static/index.html b/static/index.html index e8ff6e5..82af7cf 100644 --- a/static/index.html +++ b/static/index.html @@ -53,8 +53,12 @@

Air Quality Monitoring System

Chulalongkorn University Demonstation School

Where are you?

- Primary School - Secondary School +

Primary School

+ Card + Compact +

Secondary School

+ Card + Compact
diff --git a/static/square.html b/static/square.html new file mode 100644 index 0000000..5191121 --- /dev/null +++ b/static/square.html @@ -0,0 +1,176 @@ + + + + + + + Air Quality Dashboard + + + + + + + + + + +
+
+
+

0

+
+ +

Healthy

+
+
+ + + + + + + \ No newline at end of file diff --git a/static/vertical.html b/static/vertical.html new file mode 100644 index 0000000..e69de29