import os from typing import Any, Dict # Load environment variables from .env file try: from dotenv import load_dotenv load_dotenv() except ImportError: # python-dotenv not installed, continue without it pass try: from .exceptions import ConfigurationError from .models import DatabaseType except ImportError: # Handle case when running as standalone script class ConfigurationError(Exception): pass from enum import Enum class DatabaseType(Enum): SQLITE = "sqlite" MYSQL = "mysql" POSTGRESQL = "postgresql" INFLUXDB = "influxdb" VICTORIAMETRICS = "victoriametrics" class Config: """Configuration class for the Water Level Monitor""" # Database settings DATABASE_PATH = os.getenv("WATER_DB_PATH", "water_levels.db") # Website settings TARGET_URL = "https://hyd-app-db.rid.go.th/hydro1h.html" API_URL = "https://hyd-app-db.rid.go.th/webservice/getGroupHourlyWaterLevelReportAllHL.ashx" THAIWATER_API_KEY = os.getenv("THAIWATER_API_KEY") # Public flood notifications (ntfy). Off unless NTFY_SERVER is set. # NTFY_SERVER is what subscribers use (public https URL, shown on the # dashboard). NTFY_PUBLISH_URL is where the monitor POSTs; defaults to # NTFY_SERVER, set it to http://127.0.0.1:2586 when ntfy runs on the same # host so publishing never depends on DNS/proxy/tunnel being up. NTFY_SERVER = os.getenv("NTFY_SERVER", "").strip() NTFY_PUBLISH_URL = os.getenv("NTFY_PUBLISH_URL", "").strip() or NTFY_SERVER NTFY_TOPIC_PREFIX = os.getenv("NTFY_TOPIC_PREFIX", "ping").strip() NTFY_TOKEN = os.getenv("NTFY_TOKEN", "").strip() # publish token if ACL enabled PUBLIC_URL = os.getenv("PUBLIC_URL", "https://water.buildfor.life/").strip() REQUEST_TIMEOUT = int(os.getenv("REQUEST_TIMEOUT", "30")) USER_AGENT = ( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" ) # Database configuration # When DB_TYPE is not set explicitly, a configured Postgres connection wins over the sqlite default DB_TYPE = os.getenv( "DB_TYPE", "postgresql" if os.getenv("POSTGRES_CONNECTION_STRING") else "sqlite", ).lower() # VictoriaMetrics settings # Default to localhost; set VM_HOST in the environment for real deployments # (avoids committing infrastructure hostnames to the repo). VM_HOST = os.getenv("VM_HOST", "localhost") VM_PORT = int(os.getenv("VM_PORT", "443")) # Support for HTTPS URLs (e.g., behind reverse proxy) VM_URL = os.getenv("VM_URL") # Full URL override (e.g., https://vm.example.com) # InfluxDB settings INFLUX_HOST = os.getenv("INFLUX_HOST", "localhost") INFLUX_PORT = int(os.getenv("INFLUX_PORT", "8086")) INFLUX_DATABASE = os.getenv("INFLUX_DATABASE", "water_monitoring") INFLUX_USERNAME = os.getenv("INFLUX_USERNAME") INFLUX_PASSWORD = os.getenv("INFLUX_PASSWORD") # PostgreSQL settings POSTGRES_CONNECTION_STRING = os.getenv("POSTGRES_CONNECTION_STRING") POSTGRES_HOST = os.getenv("POSTGRES_HOST", "localhost") POSTGRES_PORT = int(os.getenv("POSTGRES_PORT", "5432")) POSTGRES_DB = os.getenv("POSTGRES_DB", "water_monitoring") POSTGRES_USER = os.getenv("POSTGRES_USER", "postgres") POSTGRES_PASSWORD = os.getenv("POSTGRES_PASSWORD") # MySQL settings MYSQL_CONNECTION_STRING = os.getenv("MYSQL_CONNECTION_STRING") # HII/ThaiWater open api-v3 collection (rainfall + backup water level) # See docs/DATA_SOURCES.md. Requires a SQL DB_TYPE (sqlite/postgresql/mysql). ENABLE_HII_COLLECTION = os.getenv("ENABLE_HII_COLLECTION", "true").lower() in ( "1", "true", "yes", ) HII_BASIN_CODE = int(os.getenv("HII_BASIN_CODE", "6")) # 6 = Ping Basin # RID large-dam daily status (app.rid.go.th/reservoir) — Mae Ngat et al. ENABLE_RESERVOIR_COLLECTION = os.getenv( "ENABLE_RESERVOIR_COLLECTION", "true" ).lower() in ("1", "true", "yes") # TTL for the /api/hii/*/latest response cache; source data changes hourly HII_CACHE_TTL_SECONDS = int(os.getenv("HII_CACHE_TTL_SECONDS", "120")) # TTL for the /measurements/latest response cache (hottest endpoint) LATEST_CACHE_TTL_SECONDS = int(os.getenv("LATEST_CACHE_TTL_SECONDS", "45")) # TTL for /health check results (includes an external RID-API probe) HEALTH_CACHE_TTL_SECONDS = int(os.getenv("HEALTH_CACHE_TTL_SECONDS", "30")) # Thread-pool size for blocking work in the web process (DB queries, # inference, health probes). Waiting threads are cheap; starving the pool # stalls every endpoint that needs a thread. EXECUTOR_THREADS = int(os.getenv("EXECUTOR_THREADS", "48")) # Web server worker processes. Above 1, uvicorn forks workers and a # localhost lock port elects a single background-collection leader. WEB_WORKERS = int(os.getenv("WEB_WORKERS", "2")) COLLECTION_LEADER_PORT = int(os.getenv("COLLECTION_LEADER_PORT", "8901")) # Umami analytics (self-hosted). The website id is public (it ships in the # dashboard