# ============================================================================= # config.py - Particulate-matter (PMS7003) monitoring module (Pi 4 Model B) config — non-secret defaults # # Operational/secret values such as server URL / API_KEY / device identifiers (DEVICE_ID/LOCATION) # are loaded from config_local.py. config_local.py is excluded from commits via .gitignore. # Setup: cp config_local.example.py config_local.py → fill in the real values # # This module integrates a PMS7003 PM sensor onto an EXISTING leak node (co-located, same # sensor_id as that node's leak sensor, distinguished by metric_type="pms7003" — exactly like # the SHT30 co-location). It attaches to BOTH leak nodes: Pi-C (sensor_id=3, the default here) # and the id=1 node (override SENSOR_ID=1 in config_local.py). Threshold/SMS is server-side. # ============================================================================= # ───────────────────────────────────────────── # Sensor identity (for server dashboard display; not secret) # SENSOR_ID is the key in the server's leak_sensor_metric / leak_sensor_status. The PMS row # shares the co-located leak sensor's id (1 or 3), separated by metric_type. Default = Pi-C (3). # ───────────────────────────────────────────── SENSOR_ID = 3 SENSOR_NAME = "Sensor 3 (PMS7003)" # ───────────────────────────────────────────── # UART config (Pi 4 40-pin header primary UART) # PMS7003 TXD → Pi RXD = GPIO15 (physical pin 10). 3.3V logic, 9600 8N1. # Port: /dev/serial0 (alias to the primary UART). Also usable: /dev/ttyS0 / /dev/ttyAMA0. # Requires: enable UART + DISABLE the serial login console (raspi-config / enable_uart=1). # ───────────────────────────────────────────── UART_PORT = "/dev/serial0" UART_BAUD = 9600 UART_TIMEOUT = 2.0 # pyserial per-read timeout (seconds) FRAME_WAIT_TIMEOUT = 5.0 # max time to wait for a valid frame in one measurement (seconds) # Force simulation. If True, runs with dummy measurements without hardware (for server integration checks). # Even if False, the driver auto-falls back to simulation when pyserial is not installed / UART is unavailable. FORCE_SIMULATION = False # ───────────────────────────────────────────── # Measurement/report intervals # ───────────────────────────────────────────── REPORT_INTERVAL = 600 # Normal measure/report interval (seconds) = 10 minutes # Matches the co-located leak node's SHT30 cadence (600s). # Server offline detection (METRIC_HEARTBEAT_TIMEOUT_SEC) must exceed this. # Override per-node in config_local.py if a different cadence is needed. MEASURE_RETRY_DELAY = 5 # Wait before retry after a failed measurement (seconds) # ───────────────────────────────────────────── # Physical plausibility range for PM values (µg/m³, atmospheric concentration) # Values outside this range are reported as metric_status="out_of_range". # * Operational threshold judgments (high PM2.5 / PM10) and SMS are all handled by the # server (config.php METRIC_PM*). This module only faithfully reports measurements. # ───────────────────────────────────────────── PM_VALUE_MIN = 0 PM_VALUE_MAX = 1000 # Module identity version (server app_version field; the server only records it, does not validate) APP_VERSION = "v2606-pms7003-rpi4" # ───────────────────────────────────────────── # Logging config # ───────────────────────────────────────────── LOG_FILE = "/var/log/pms7003_sensor.log" LOG_MAX_BYTES = 5 * 1024 * 1024 # 5MB LOG_BACKUP_COUNT = 3 # ───────────────────────────────────────────── # HTTP retry config # ───────────────────────────────────────────── HTTP_TIMEOUT = 10 # Single request timeout (seconds) HTTP_RETRY_COUNT = 3 # Number of retries HTTP_RETRY_DELAY = 5 # Wait between retries (seconds) # ───────────────────────────────────────────── # Load operational/secret values (fail closed) # If config_local.py is missing, abort immediately with a clear error instead of # starting incorrectly with placeholder/hardcoded secrets. # ───────────────────────────────────────────── try: from config_local import ( # noqa: F401 (pms7003_sensor.py re-imports from config) API_URL, API_KEY, DEVICE_ID, DEVICE_LOCATION, ) except ImportError as e: raise RuntimeError( "config_local.py not found. " "Run `cp config_local.example.py config_local.py`, then fill in the real server URL / API_KEY / " "device info. (Never commit secret values.)" ) from e # ───────────────────────────────────────────── # Optional override — per-device values from config_local.py. # Editing the tracked config.py per device causes deployment drift, so each leak node # overrides SENSOR_ID / SENSOR_NAME here instead (Pi-C keeps id 3; the id=1 node sets # SENSOR_ID=1). If config_local defines neither, the defaults above (SENSOR_ID=3) are kept. # ───────────────────────────────────────────── try: from config_local import SENSOR_ID # noqa: F811 — per-device sensor_id override except ImportError: pass try: from config_local import SENSOR_NAME # noqa: F811 — per-device display name override except ImportError: pass try: # Per-device report cadence. Defaults to 600s (10 min) to match the co-located leak # node's SHT30 cadence; override here if a node needs a different interval. from config_local import REPORT_INTERVAL # noqa: F811 — per-device report interval override except ImportError: pass try: from config_local import UART_PORT # noqa: F811 — per-device UART device override except ImportError: pass # Validate (fail closed) — a typo must not silently start with a wrong/plausible id. if not (isinstance(SENSOR_ID, int) and 1 <= SENSOR_ID <= 255): raise RuntimeError(f"SENSOR_ID must be int 1..255 (check config_local.py): {SENSOR_ID!r}") if not (isinstance(SENSOR_NAME, str) and SENSOR_NAME): raise RuntimeError("SENSOR_NAME must be a non-empty string (check config_local.py).") if not (isinstance(REPORT_INTERVAL, int) and REPORT_INTERVAL > 0): raise RuntimeError(f"REPORT_INTERVAL must be a positive int (seconds) (check config_local.py): {REPORT_INTERVAL!r}") if not (isinstance(UART_PORT, str) and UART_PORT): raise RuntimeError("UART_PORT must be a non-empty string (check config_local.py).")