Skip to content

Commit

Permalink
Add configuration via environment variable
Browse files Browse the repository at this point in the history
  • Loading branch information
TimOrme committed Mar 20, 2023
1 parent bca9422 commit 98b9287
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 23 deletions.
5 changes: 5 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
# Ruff version.
rev: 'v0.0.257'
hooks:
- id: ruff
- repo: https://github.com/psf/black
rev: 22.10.0
hooks:
Expand Down
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,21 @@ sudo systemctl daemon-reload
sudo systemctl start aqimon
```

## Configuration

| Variable | Default | Description |
|------------------------------|---------------------|-----------------------------------------------------------------------------------------------------------------------------------|
| AQIMON_DB_PATH | ~/.aqimon/db.sqlite | The path to the database file, where read information is stored. It should be an absolute path; user home expansion is supported. |
| AQIMON_POLL_FREQUENCY_SEC | 900 (15 minutes) | Sets how frequently to read from the device, in seconds. |
| AQIMON_RETENTION_MINUTES | 10080 (1 week) | Sets how long data will be kept in the database, in minutes. |
| AQIMON_READER_TYPE | NOVAPM | The reader type to use, either NOVAPM or MOCK. |
| AQIMON_USB_PATH | /dev/ttyUSB0 | The path to the USB device for the sensor. |
| AQIMON_SLEEP_TIME_SEC | 5 | The number of seconds to wait for between each read in a set of reads. |
| AQIMON_SAMPLE_COUNT_PER_READ | 5 | The number of reads to take with each sample. |
| AQIMON_SERVER_PORT | 8000 | The port to run the server on. |
| AQIMON_SERVER_HOST | 0.0.0.0 | The host to run the server on. |


## Contributing

### Toolset
Expand Down Expand Up @@ -100,6 +115,15 @@ To run auto-formatters, run:
just format
```

### Using the Mock Reader

Aqimon ships with a mock reader class that you can use in the event that you don't have a reader available on your
development computer. The mock reader just returns randomized reads. To use it, you can start the server like:

```commandline
AQIMON_READER_TYPE=MOCK poetry run aqimon
```

### Submitting a PR

Master branch is locked, but you can open a PR on the repo. Build checks must pass, and changes approved by a code
Expand Down
36 changes: 19 additions & 17 deletions aqimon/config.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
"""Config module."""
import os.path
from typing import Optional
from dataclasses import dataclass
from serde import serde
from serde.toml import to_toml, from_toml

DEFAULT_CONFIG_PATH = "~/.aqimon/config"
DEFAULT_DB_PATH = "~/.aqimon/db.sqlite"


Expand All @@ -25,6 +23,10 @@ class Config:
usb_sleep_time_sec: int
sample_count_per_read: int

# Server properties
server_port: int
server_host: str


DEFAULT_CONFIG = Config(
database_path=os.path.expanduser(DEFAULT_DB_PATH),
Expand All @@ -34,6 +36,8 @@ class Config:
usb_path="/dev/ttyUSB0",
usb_sleep_time_sec=5,
sample_count_per_read=5,
server_port=8000,
server_host="0.0.0.0",
)


Expand All @@ -49,18 +53,16 @@ def save_config(config: Config, path: str):
file.write(to_toml(config))


def get_config(passed_config_path: Optional[str]) -> Config:
"""Get the config.
If a toml config file path is passed, it is loaded and used.
If no toml config is passed, a default config path is used, if the toml file exists.
If no toml exists in the default location, a sensible default config is loaded.
"""
if passed_config_path and os.path.exists(passed_config_path):
return _load_config(passed_config_path)
elif not passed_config_path and os.path.exists(DEFAULT_CONFIG_PATH):
return _load_config(DEFAULT_CONFIG_PATH)
else:
return DEFAULT_CONFIG
def get_config_from_env() -> Config:
"""Get the config from environment variables."""
return Config(
database_path=os.path.expanduser(os.environ.get("AQIMON_DB_PATH", DEFAULT_CONFIG.database_path)),
poll_frequency_sec=int(os.environ.get("AQIMON_POLL_FREQUENCY_SEC", DEFAULT_CONFIG.poll_frequency_sec)),
retention_minutes=int(os.environ.get("AQIMON_RETENTION_MINUTES", DEFAULT_CONFIG.retention_minutes)),
reader_type=os.environ.get("AQIMON_READER_TYPE", DEFAULT_CONFIG.reader_type),
usb_path=os.environ.get("AQIMON_USB_PATH", DEFAULT_CONFIG.usb_path),
usb_sleep_time_sec=int(os.environ.get("AQIMON_USB_SLEEP_TIME_SEC", DEFAULT_CONFIG.usb_sleep_time_sec)),
sample_count_per_read=int(os.environ.get("AQIMON_SAMPLE_COUNT_PER_READ", DEFAULT_CONFIG.sample_count_per_read)),
server_port=int(os.environ.get("AQIMON_SERVER_PORT", DEFAULT_CONFIG.server_port)),
server_host=os.environ.get("AQIMON_SERVER_HOST", DEFAULT_CONFIG.server_host),
)
10 changes: 6 additions & 4 deletions aqimon/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@
from .read.mock import MockReader
from .read.novapm import NovaPmReader
from . import aqi_common
from .config import Config, get_config
from .config import Config, get_config_from_env
import logging

log = logging.getLogger(__name__)

app = FastAPI()
config = get_config(None)
config = get_config_from_env()


project_root = Path(__file__).parent.resolve()
Expand Down Expand Up @@ -145,9 +145,11 @@ async def status():

def start():
"""Start the server."""
uvicorn.run(app, host="0.0.0.0", port=8000)
env_config = get_config_from_env()
uvicorn.run(app, host=env_config.server_host, port=env_config.server_port)


def debug():
"""Start the server in debug mode, with hotswapping code."""
uvicorn.run("aqimon.server:app", host="0.0.0.0", port=8000, reload=True)
env_config = get_config_from_env()
uvicorn.run("aqimon.server:app", host=env_config.server_host, port=env_config.server_port, reload=True)
1 change: 1 addition & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ compile_elm:
lint:
black --check .
ruff check .
mypy .
elm-format --validate elm/

# Format code
Expand Down
49 changes: 48 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pyserde = {extras = ["toml"], version = "^0.10.2"}
[tool.poetry.group.dev.dependencies]
ruff = "^0.0.256"
black = "^23.1.0"
mypy = "^1.1.1"

[build-system]
requires = ["poetry-core"]
Expand All @@ -34,4 +35,8 @@ ignore = ["D203", "D213"]
line-length = 120

[tool.black]
line-length = 120
line-length = 120

[[tool.mypy.overrides]]
module = "serial.*"
ignore_missing_imports = true

0 comments on commit 98b9287

Please sign in to comment.