diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..4ad9833 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,9 @@ +repos: +- repo: https://github.com/psf/black + rev: 22.10.0 + hooks: + - id: black +- repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.1.1 + hooks: + - id: mypy \ No newline at end of file diff --git a/aqimon/aqi_common.py b/aqimon/aqi_common.py index ab25cf8..af33509 100644 --- a/aqimon/aqi_common.py +++ b/aqimon/aqi_common.py @@ -1,5 +1,23 @@ -AQI = [(0, 50), (51, 100), (101, 150), (151, 200), (201, 300), (301, 400), (401-500)] -PM_25 = [(0.0, 12.0), (12.1, 35.4), (35.5, 55.4), (55.5, 150.4), (150.5, 250.4), (250.5, 350.4), (350.5, 500.4)] +from typing import List, Tuple + +AQI: List[Tuple[int, int]] = [ + (0, 50), + (51, 100), + (101, 150), + (151, 200), + (201, 300), + (301, 400), + (401, 500), +] +PM_25: List[Tuple[float, float]] = [ + (0.0, 12.0), + (12.1, 35.4), + (35.5, 55.4), + (55.5, 150.4), + (150.5, 250.4), + (250.5, 350.4), + (350.5, 500.4), +] COLORS = ["green", "yellow", "orange", "red", "purple", "dark-red", "dark-red"] @@ -21,11 +39,8 @@ def calculate_epa_aqi(pm_25_read: float) -> int: aqi_high = AQI[i][1] pm_low = pm_range[0] pm_high = pm_range[1] - epa = ((aqi_high - aqi_low) / (pm_high - pm_low)) * (pm_25_read - pm_low) + aqi_low + epa = ((aqi_high - aqi_low) / (pm_high - pm_low)) * ( + pm_25_read - pm_low + ) + aqi_low return round(epa) return -1 - - - - - diff --git a/aqimon/config.py b/aqimon/config.py index 6efdc86..b492c34 100644 --- a/aqimon/config.py +++ b/aqimon/config.py @@ -25,14 +25,12 @@ class Config: DEFAULT_CONFIG = Config( database_path=os.path.expanduser(DEFAULT_DB_PATH), - poll_frequency_sec=60*15, # Every 15 minutes - retention_minutes=60*24*7, # 1 week - + poll_frequency_sec=60 * 15, # Every 15 minutes + retention_minutes=60 * 24 * 7, # 1 week reader_type="NOVAPM", - usb_path="/dev/usbtty0", usb_sleep_time_sec=5, - sample_count_per_read=5 + sample_count_per_read=5, ) diff --git a/aqimon/database.py b/aqimon/database.py index f2c02b2..d2bc361 100644 --- a/aqimon/database.py +++ b/aqimon/database.py @@ -4,7 +4,9 @@ async def get_latest_stats(dbconn: databases.Database): - result = await dbconn.fetch_one("SELECT * FROM aqi_log ORDER BY event_time DESC LIMIT 1") + result = await dbconn.fetch_one( + "SELECT * FROM aqi_log ORDER BY event_time DESC LIMIT 1" + ) if result: return result[0], result[1], result[2], result[3] else: @@ -19,19 +21,32 @@ async def get_all_stats(dbconn: databases.Database): async def clean_old(dbconn: databases.Database, retention_minutes: int): last_week = datetime.now() - timedelta(minutes=retention_minutes) last_week_timestamp = int(last_week.timestamp()) - await dbconn.execute("DELETE FROM aqi_log WHERE event_time < :last_week_timestamp", values={"last_week_timestamp": last_week_timestamp}) + await dbconn.execute( + "DELETE FROM aqi_log WHERE event_time < :last_week_timestamp", + values={"last_week_timestamp": last_week_timestamp}, + ) -async def add_entry(dbconn: databases.Database, event_time, epa_aqi_pm25, raw_pm25, raw_pm10): +async def add_entry( + dbconn: databases.Database, event_time, epa_aqi_pm25, raw_pm25, raw_pm10 +): formatted_time = int(event_time.timestamp()) - await dbconn.execute(query="INSERT INTO aqi_log VALUES (:formatted_time, :epa_aqi_pm25, :raw_pm25, :raw_pm10)", - values={"formatted_time": formatted_time, - "epa_aqi_pm25": epa_aqi_pm25, - "raw_pm25": raw_pm25, - "raw_pm10": raw_pm10}) + await dbconn.execute( + query="INSERT INTO aqi_log VALUES (:formatted_time, :epa_aqi_pm25, :raw_pm25, :raw_pm10)", + values={ + "formatted_time": formatted_time, + "epa_aqi_pm25": epa_aqi_pm25, + "raw_pm25": raw_pm25, + "raw_pm10": raw_pm10, + }, + ) async def create_tables(dbconn: databases.Database): # Create table - await dbconn.execute('''CREATE TABLE IF NOT EXISTS aqi_log (event_time integer, epa_aqi_pm25 real, raw_pm25 real, raw_pm10 real)''') - await dbconn.execute('''CREATE INDEX IF NOT EXISTS aqi_eventtime ON aqi_log (event_time)''') + await dbconn.execute( + """CREATE TABLE IF NOT EXISTS aqi_log (event_time integer, epa_aqi_pm25 real, raw_pm25 real, raw_pm10 real)""" + ) + await dbconn.execute( + """CREATE INDEX IF NOT EXISTS aqi_eventtime ON aqi_log (event_time)""" + ) diff --git a/aqimon/read/novapm.py b/aqimon/read/novapm.py index 0ae7e44..991c7e8 100644 --- a/aqimon/read/novapm.py +++ b/aqimon/read/novapm.py @@ -40,6 +40,6 @@ def _averaged_read(self) -> AqiRead: def _read(self) -> AqiRead: ser = serial.Serial(self.usb_path) data = ser.read(10) - pmtwofive = int.from_bytes(data[2:4], byteorder='little') / 10 - pmten = int.from_bytes(data[4:6], byteorder='little') / 10 + pmtwofive = int.from_bytes(data[2:4], byteorder="little") / 10 + pmten = int.from_bytes(data[4:6], byteorder="little") / 10 return AqiRead(pmtwofive, pmten) diff --git a/aqimon/server.py b/aqimon/server.py index b09ff78..c7b4b4a 100644 --- a/aqimon/server.py +++ b/aqimon/server.py @@ -5,11 +5,16 @@ from fastapi.staticfiles import StaticFiles from fastapi_utils.tasks import repeat_every import uvicorn -import datetime import databases from pathlib import Path from datetime import datetime -from .database import get_latest_stats, get_all_stats, add_entry, create_tables, clean_old +from .database import ( + get_latest_stats, + get_all_stats, + add_entry, + create_tables, + clean_old, +) from .read import AqiRead, Reader, ReaderState from .read.mock import MockReader from .read.novapm import NovaPmReader @@ -26,12 +31,16 @@ def _get_reader(conf: Config) -> Reader: - if conf.reader_type == 'MOCK': + if conf.reader_type == "MOCK": return MockReader() - elif conf.reader_type == 'NOVAPM': - return NovaPmReader(usb_path=conf.usb_path, - iterations=conf.sample_count_per_read, - sleep_time=conf.usb_sleep_time_sec) + elif conf.reader_type == "NOVAPM": + return NovaPmReader( + usb_path=conf.usb_path, + iterations=conf.sample_count_per_read, + sleep_time=conf.usb_sleep_time_sec, + ) + else: + raise Exception("Invalid reader type specified") reader = _get_reader(config) @@ -52,7 +61,13 @@ async def read_from_device() -> None: result: AqiRead = reader.read() event_time = datetime.now() epa_aqi_pm25 = aqi_common.calculate_epa_aqi(result.pmtwofive) - await add_entry(dbconn=database, event_time=event_time, epa_aqi_pm25=epa_aqi_pm25, raw_pm25=result.pmtwofive, raw_pm10=result.pmten) + await add_entry( + dbconn=database, + event_time=event_time, + epa_aqi_pm25=epa_aqi_pm25, + raw_pm25=result.pmtwofive, + raw_pm10=result.pmten, + ) await clean_old(dbconn=database, retention_minutes=config.retention_minutes) reader_state = ReaderState(True, None) except Exception as e: @@ -71,7 +86,7 @@ def convert_all_to_view_dict(results): view["pm25"] = [] view["pm10"] = [] for x in results: - view["t"].append(datetime.fromtimestamp(int(x[0])).strftime('%m-%d %H:%M')) + view["t"].append(datetime.fromtimestamp(int(x[0])).strftime("%m-%d %H:%M")) view["epa"].append(x[1]) view["pm25"].append(x[2]) view["pm10"].append(x[3]) @@ -82,7 +97,13 @@ def convert_all_to_view_dict(results): async def add_new_entry() -> AqiRead: data = reader.read() epa_aqi_pm25 = aqi_common.calculate_epa_aqi(data.pmtwofive) - await add_entry(dbconn=database, event_time=datetime.now(), epa_aqi_pm25=epa_aqi_pm25, raw_pm25=data.pmtwofive, raw_pm10=data.pmten) + await add_entry( + dbconn=database, + event_time=datetime.now(), + epa_aqi_pm25=epa_aqi_pm25, + raw_pm25=data.pmtwofive, + raw_pm10=data.pmten, + ) return data @@ -93,18 +114,19 @@ async def home(request: Request): all_stats = await get_all_stats(database) all_json = json.dumps(convert_all_to_view_dict(all_stats)) aqi_color = aqi_common.get_color_from_level(level) - return templates.TemplateResponse("index.html", { - "request": request, - "reader_alive": reader_state.alive, - "reader_exception": reader_state.last_exception, - "all_log":all_json, - "aqi": aqi, - "pm25": pm25, - "pm10": pm10, - "aqi_color": aqi_color}) - - - + return templates.TemplateResponse( + "index.html", + { + "request": request, + "reader_alive": reader_state.alive, + "reader_exception": reader_state.last_exception, + "all_log": all_json, + "aqi": aqi, + "pm25": pm25, + "pm10": pm10, + "aqi_color": aqi_color, + }, + ) def start():