POSA_LEAKSMS/raspberry_pi4/config.py
cw2007 ec78be0bda feat(pms7003): 누수 노드에 미세먼지(PM2.5/PM10) 통합
센서1·3(누수 노드)에 PMS7003 를 온습도와 동일 sensor_id 로 통합하고 metric_type='pms7003'
로 구분해 수집한다. LED 는 스위칭 없이 상시 점등 방식으로 확정.

- raspberry_pi4_pms7003/: PMS7003 노드 풀세트(드라이버·센서·install·systemd·vendor·배선·연결테스트·프레임 단위테스트)
- 서버: evaluate_metric_thresholds() 에 pm25/pm10 선택 인자 추가(온습도 전용 호출 후방호환),
  sensor_data.php 수집 경로에 pms7003 metric 반영, config.php/예시에 PM 임계 추가
- SQL: migration_pms7003.sql · schema_sht30.sql 정합, tools/migration_leak_add_{pms7003,sht30} 마이그레이터
- tests/pm_threshold_test.php + run_tests.sh 러너 등록
- .gitignore: 노드별 config_local.py 커밋 차단
2026-07-19 10:46:33 +09:00

121 lines
7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# =============================================================================
# config.py - Leak Detection System v2 (Pi 4 Model B) settings — non-secret defaults
#
# Operational/secret values such as server URL / API_KEY / device identifier 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 with real values
# =============================================================================
# ─────────────────────────────────────────────
# Sensor GPIO settings (BCM numbering — same as Pi 4 40-pin header)
# GPIO17 = physical pin 11 on the 40-pin header
# ─────────────────────────────────────────────
SENSORS = [
{"id": 1, "gpio": 17, "name": "Sensor 1 (Server Room)"},
# Add more sensors below if present (BCM numbering)
# {"id": 2, "gpio": 27, "name": "Sensor 2 (Machine Room)"}, # physical pin 13
# {"id": 3, "gpio": 22, "name": "Sensor 3 (Restroom)"}, # physical pin 15
]
# Sensor active-level setting
# When using the e-con 5C-08 resistive sensor + 10MΩ pull-down:
# Dry: GPIO = LOW(0) → ACTIVE_LOW = False
# Leak: GPIO = HIGH(1)
ACTIVE_LOW = False
# GPIO internal pull-resistor setting
# Must stay False (PUD_OFF)!
# Enabling the internal pull-down (50kΩ) keeps the line from going HIGH even on a leak, causing detection failure.
# → Use only the external 10MΩ pull-down resistor
USE_INTERNAL_PULL = False # True = PUD_DOWN, False = PUD_OFF
# ─────────────────────────────────────────────
# Interval settings
# ─────────────────────────────────────────────
REPORT_INTERVAL = 600 # Normal-state periodic report interval (sec) = 10 min
# Must be shorter than the server's HEARTBEAT_TIMEOUT_SEC (default 1200 sec)
# to avoid offline false positives (600 sec = offline after 2 missed reports).
LEAK_REPORT_INTERVAL = 10 # Re-report interval while a leak is detected (sec)
DEBOUNCE_MS = 500 # Debounce time (ms) — set generously due to high impedance
# Confirm-read debounce: an edge alone never changes state. After an edge the pin is
# re-read CONFIRM_READS times, CONFIRM_INTERVAL_SEC apart; ALL reads must agree with the
# new state or the event is discarded as noise. Real water persists; sub-second EMI
# blips on the 10MΩ high-impedance line (server-room noise) do not.
# Total confirmation latency ≈ CONFIRM_READS × CONFIRM_INTERVAL_SEC ≈ 0.9 sec.
CONFIRM_READS = 3 # Consecutive identical reads required to accept a change
CONFIRM_INTERVAL_SEC = 0.3 # Delay between confirmation reads (sec)
# ─────────────────────────────────────────────
# Log settings
# ─────────────────────────────────────────────
LOG_FILE = "/var/log/leak_sensor.log"
LOG_MAX_BYTES = 5 * 1024 * 1024 # 5MB
LOG_BACKUP_COUNT = 3
# ─────────────────────────────────────────────
# Retry settings
# ─────────────────────────────────────────────
HTTP_TIMEOUT = 10 # HTTP request timeout (sec)
HTTP_RETRY_COUNT = 3 # Retry count
HTTP_RETRY_DELAY = 5 # Retry delay (sec)
# ─────────────────────────────────────────────
# Camera settings
# Pi 4 = 15-pin (1.0mm pitch) standard CSI, Zero 2 W = 22-pin (0.5mm pitch) CSI →
# ribbon/camera board not compatible (needs a Pi 4 15-pin ribbon or a 15↔22 adapter).
# Capture uses rpicam-still (Bookworm) / libcamera-still (Bullseye).
# ─────────────────────────────────────────────
CAMERA_ENABLED = True # Set to False to skip capture/upload
PHOTO_WIDTH = 640 # Capture resolution (width)
PHOTO_HEIGHT = 480 # Capture resolution (height)
PHOTO_QUALITY = 60 # JPEG quality (1-100, 60 ≈ 50-80KB)
# ─────────────────────────────────────────────
# Load operational/secret values (fail closed)
# If config_local.py is missing, stop immediately with a clear error
# instead of wrongly starting with placeholder/hardcoded secrets.
# ─────────────────────────────────────────────
try:
from config_local import ( # noqa: F401 (leak_sensor.py re-imports from config)
API_URL,
API_KEY,
PHOTO_UPLOAD_URL,
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 can be overridden in config_local.py.
# Editing the tracked file (config.py) per device causes deployment drift, so
# define SENSORS in config_local.py for a new site node (e.g. sensor_id 3).
# ─────────────────────────────────────────────
try:
from config_local import SENSORS # noqa: F811 — per-device sensor list override
except ImportError:
pass # use the default above if config_local has no SENSORS
# SENSORS format validation (fail closed) — prevents an override typo/format error from
# silently starting with a wrong sensor_id (e.g. default 1) and overwriting another site's state.
if not isinstance(SENSORS, list) or not SENSORS:
raise RuntimeError("SENSORS must be a non-empty list (check config_local.py).")
_ids, _gpios = set(), set()
for _s in SENSORS:
if not (isinstance(_s, dict)
and isinstance(_s.get("id"), int) and 1 <= _s["id"] <= 255
and isinstance(_s.get("gpio"), int)
and isinstance(_s.get("name"), str) and _s["name"]):
raise RuntimeError(
f"SENSORS entry format error: {_s!r} — id(int 1..255)/gpio(int)/name(str) required."
)
if _s["id"] in _ids or _s["gpio"] in _gpios:
raise RuntimeError(f"SENSORS duplicate id/gpio: {_s!r}")
_ids.add(_s["id"])
_gpios.add(_s["gpio"])
del _ids, _gpios