누수 감지↔복구 플래핑 SMS 폭탄 방지, 온습도 측정 주기 1시간화(부수 오탐
차단), 센서별 SMS 수신자 그룹 지정 3종을 함께 반영한다.
1) 누수 디바운스 (서버 전용, 펌웨어 무변경)
- leak_sensor_status 에 leak_confirmed / leak_pending_since 컬럼 추가.
- leak_debounce_decide(): 단일 대칭 타이머 순수 함수. raw 상태가 확인창
동안 지속돼야 확정하고 확정 엣지에서만 SMS 1회. dry 블립<복구창이면
복구 미확정 → 확정 유지 → 재감지·복구 SMS 둘 다 차단(폭탄 원천 봉쇄).
- apply_leak_debounce()/sweep_pending_leak_transitions(): 센서 네임드 락으로
직렬화, cron sweep 으로 보고 끊겨도 확정 해소. 사고(leak_incident)
open/close 도 확정 엣지로 이동(월간보고 사고건수 정합). raw 이벤트는
leak_sensor_log 에 그대로 보존(플래핑 진단).
- 컬럼 없는 구배포는 레거시 즉시 발송으로 graceful degrade.
- 확인창 기본: 감지 60s / 복구 180s (config.local.php 조정).
2) 온습도 1시간 주기 + 센서별 히트비트 타임아웃
- raspberry_pi4_sht30/config.py REPORT_INTERVAL 300→3600.
- heartbeat_timeout_for(): 명시 오버라이드 > 온습도 기본(7500s) > 전역(1200s).
cron_heartbeat / dashboard 오프라인 판정을 센서별로. 매시간 오탐 제거,
누수 오프라인은 20분 유지.
- STUCK_DETECT_SAMPLES 6→3(1시간 주기 ≈3시간), 관련 주석 정정.
3) 센서별 수신자 그룹
- SENSOR_RECIPIENTS 맵 + recipients_for_sensor() 전역 폴백.
- sms_dispatch_to_recipients 가 센서별로 수신자 해소(에스컬레이션 TIER2
명시 전달은 유지, 헬스/잔액은 전역).
대시보드: 누수 카드 '확인중(디바운스)' 배지, 시스템 탭에 확인창·센서별
타임아웃·수신자 그룹 표시.
마이그레이션: sql/migration_leak_debounce.sql (컬럼 2개), schema_sht30.sql 갱신.
테스트: leak_debounce(12) / recipients_group / heartbeat_timeout 순수 단위 추가.
설계: docs/superpowers/specs/2026-07-13-leak-debounce-sensor-config-design.md
85 lines
5.2 KiB
Python
85 lines
5.2 KiB
Python
# =============================================================================
|
|
# config.py - Temperature/Humidity (SHT30) 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 is "Module 2" (temperature/humidity). The server (php/) dashboard shows sensor_id=2 as the
|
|
# default temperature/humidity sensor, so the SENSOR_ID default is set to 2.
|
|
# =============================================================================
|
|
|
|
# ─────────────────────────────────────────────
|
|
# Sensor identity (for server dashboard display; not secret)
|
|
# SENSOR_ID is the key in the server's leak_sensor_metric / leak_sensor_status. Dashboard default chart=2.
|
|
# ─────────────────────────────────────────────
|
|
SENSOR_ID = 2
|
|
SENSOR_NAME = "Sensor 2 (SHT30)"
|
|
|
|
# ─────────────────────────────────────────────
|
|
# I2C bus config (Pi 4 40-pin header)
|
|
# SDA = GPIO2 (physical pin 3), SCL = GPIO3 (physical pin 5) → I2C bus 1
|
|
# ADDR pin: GND=0x44, VDD=0x45
|
|
# ─────────────────────────────────────────────
|
|
I2C_BUS = 1
|
|
I2C_ADDR = 0x44
|
|
|
|
# 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 smbus2 is not installed / I2C is disabled.
|
|
FORCE_SIMULATION = False
|
|
|
|
# ─────────────────────────────────────────────
|
|
# Measurement/report intervals (same defaults as firmware app_config.h)
|
|
# ─────────────────────────────────────────────
|
|
REPORT_INTERVAL = 3600 # Normal measure/report interval (seconds) = 1 hour
|
|
# Server offline detection must exceed this: METRIC_HEARTBEAT_TIMEOUT_SEC
|
|
# (default 7500s ≈ 2 missed hourly reports + margin) covers it.
|
|
# 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 temp/humidity (same as firmware classify_metric_status)
|
|
# Values outside this range are reported as metric_status="out_of_range".
|
|
# * Operational threshold judgments (high/low temp, high/low humidity) and SMS are all handled
|
|
# by the server (config.php METRIC_*). This module only faithfully reports measurements.
|
|
# ─────────────────────────────────────────────
|
|
TEMP_LOW_C = -40.0
|
|
TEMP_HIGH_C = 125.0
|
|
RH_LOW = 0.0
|
|
RH_HIGH = 100.0
|
|
|
|
# Module identity version (server app_version field; the server only records it, does not validate)
|
|
APP_VERSION = "v2606-sht30-rpi4"
|
|
|
|
# ─────────────────────────────────────────────
|
|
# Logging config
|
|
# ─────────────────────────────────────────────
|
|
LOG_FILE = "/var/log/sht30_sensor.log"
|
|
LOG_MAX_BYTES = 5 * 1024 * 1024 # 5MB
|
|
LOG_BACKUP_COUNT = 3
|
|
|
|
# ─────────────────────────────────────────────
|
|
# HTTP retry config (same defaults as firmware APP_HTTP_*)
|
|
# ─────────────────────────────────────────────
|
|
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 (sht30_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
|