'localhost', 'DB_PORT' => 3306, 'DB_NAME' => 'your_db_name', 'DB_USER' => 'your_db_user', 'DB_PASS' => 'your_db_password', 'API_KEY' => 'replace-with-long-random-secret', // 요청 서명은 HMAC-SHA256(키=API_KEY, 메시지=요청본문 바이트)이며 X-Signature 헤더로 // 전송된다. 레거시 키-접두 sha256 폴백은 기본 비활성이고, 운영(STM32 HMAC 전용)에서는 // 반드시 false 로 둔다. 과거 RPi(레거시) 클라이언트가 아직 남아 있을 때만 true 로 켠다. 'ALLOW_LEGACY_BODY_SIGNATURE' => false, 'SMS_USER_ID' => 'your-cafe24-sms-user-id', 'SMS_SECURE' => 'your-cafe24-sms-secure-key', 'SMS_SENDER' => '01000000000', 'SMS_RECIPIENTS' => [ '01000000000', ], // 센서별 수신자 그룹 — 지정한 센서의 경보 SMS 는 그 그룹으로만 발송된다. 지정 없는 // 센서는 위 전역 SMS_RECIPIENTS 로 폴백한다(헬스/잔액 등 시스템 통지도 전역 사용). // 예: 누수(1번)는 시설팀, 3번은 고객센터로. 비우면(기본) 전역만 사용. 'SENSOR_RECIPIENTS' => [ // 1 => ['010-1111-1111', '010-2222-2222'], // 3 => ['010-3333-3333'], ], 'ADMIN_USER' => 'admin', 'ADMIN_PASSWORD_HASH' => '$2y$10$replace.with.password_hash.output', // Base32 TOTP secret. MUST be left EMPTY here and set to a UNIQUE generated value in // config.local.php. Empty '' => admin_mfa_configured() is false => login is DISABLED // (fail-closed) until a real secret is set. NEVER put a literal Base32 string in this // example file: any non-empty Base32-ish value (even "REPLACE_WITH_...") decodes to a // usable, publicly-known TOTP secret and silently enables MFA on a known seed. // Generate a unique secret on the server only: // php -r "require 'php/admin_security.php'; echo admin_totp_secret().PHP_EOL;" 'ADMIN_TOTP_SECRET' => '', // Temporary bootstrap token for setup_mfa.php. Remove or empty after MFA enrollment. 'MFA_SETUP_TOKEN' => 'replace-with-temporary-random-token', // 누수 모듈 sensor_id 목록 — 대시보드 누수 상태 카드 표시 대상 (기본 [1]). // 신규 누수 지점(예: Pi-C, sensor_id 3) 투입 시: [1, 3] 'LEAK_SENSOR_IDS' => [1], // 온습도(SHT30) sensor_id 목록 — 대시보드 온습도 카드 표시 대상 (기본 [2]). // 누수 노드(1·3)에 SHT30 을 얹어 "같은 sensor_id" 로 온습도를 보고하는 구성에서는 // 1·3 도 포함한다: [1, 2, 3]. (같은 지점에 누수 카드 + 온습도 카드가 함께 표시됨) 'METRIC_SENSOR_IDS' => [1, 2, 3], // 누수 감지/복구 디바운스(감지↔복구 플래핑 SMS 폭탄 방지). 상태가 확인창 동안 지속돼야 // 확정하고 확정 엣지에서만 SMS 1회. 아래는 기본값(필요 시 조정). sql/migration_leak_debounce.sql // 적용 필요(컬럼 없으면 레거시 즉시 발송으로 자동 폴백). // 'LEAK_DEBOUNCE_ENABLED' => true, // 'LEAK_ALERT_CONFIRM_SEC' => 60, // 감지 확인창(초) // 'LEAK_RECOVERY_CONFIRM_SEC' => 180, // 복구 확인창(초) // 센서별 오프라인 판정 타임아웃(무수신 초). 온습도(SHT30)는 1시간 주기라 전역 20분으론 // 매시간 오탐 → 온습도 센서는 METRIC_HEARTBEAT_TIMEOUT_SEC(기본 7500s≈125분) 자동 적용. // 특정 센서만 다르게 하려면 SENSOR_HEARTBEAT_TIMEOUT_SEC 로 명시 오버라이드. // 'METRIC_HEARTBEAT_TIMEOUT_SEC' => 7500, // ★ 동일-id 온습도 통합: 1·3 은 위 METRIC_SENSOR_IDS 에도 들어가지만 누수 노드이므로 // metric 기본(7500s)으로 느슨해지면 안 됨 → 누수 타임아웃(30분) 유지하도록 명시 오버라이드. 'SENSOR_HEARTBEAT_TIMEOUT_SEC' => [1 => 1800, 3 => 1800], // 온습도 임계 경보 (서버 판정 → SMS). 운영 환경에 맞게 조정. 'METRIC_TEMP_HIGH_C' => 27, // 고온 임계(℃) 'METRIC_TEMP_LOW_C' => 10, // 저온 임계(℃) 'METRIC_RH_HIGH' => 60, // 고습 임계(%) 'METRIC_RH_LOW' => -1, // 저습 경보 비활성(RH 는 항상 ≥0 → 절대 발화 안 함) 'METRIC_DEWPOINT_MARGIN_C' => 0, // 결로(이슬점) 경보 비활성(0 이면 판정 분기 스킵) 'METRIC_ALERT_COOLDOWN_SEC' => 1800, // 동일 종류 경보 재발송 억제(초, 기본 30분) // 예보·대응(대시보드 예방 신호 + 조치 가이드). 전부 기본값으로 동작 — 필요 시만 override. // FORECAST_ENABLED(true), FORECAST_WARN_MARGIN_TEMP_C(2.0), FORECAST_WARN_MARGIN_RH(8.0), // FORECAST_HORIZON_MIN(180), FORECAST_RATE_TEMP_C_PER_10MIN(3.0) 등. config.php 참고. // 대응 담당자(대시보드 '대응 필요' 섹션에 표시). 비우면 담당자 블록 생략. 'RESPONSE_CONTACTS' => [ // ['role' => '시설담당', 'name' => '홍길동', 'phone' => '010-0000-0000'], // ['role' => '야간당직', 'name' => '김철수', 'phone' => '010-0000-0000'], ], // 상태별 대응 체크리스트는 config.php 의 기본 플레이북을 사용. 현장 절차로 바꾸려면 // 'RESPONSE_PLAYBOOKS' => ['high_temp' => ['title'=>'고온 대응','steps'=>['...']], ...] 로 통째 override. // 미복귀 경보 2차(야간) 에스컬레이션 수신자. 비우면 비활성(opt-in). 'SMS_RECIPIENTS_TIER2' => [], 'ESCALATE_AFTER_SEC' => 1800, // 경보 미복귀 30분 후 2차 통지 // 아래는 기본값으로 동작(필요 시에만 override): // SMS_BALANCE_WARN_THRESHOLD(30건), HEALTH_SMS_HOUR(9시), // STUCK_DETECT_SAMPLES(3 = 온습도 1시간 주기 기준 ≈3시간), SMS_RETRY_OFFLINE(true) 'SMS_LOG_RETENTION_DAYS' => 365, 'SENSOR_LOG_RETENTION_DAYS' => 365, 'SENSOR_METRIC_RETENTION_DAYS' => 365, 'ADMIN_AUDIT_RETENTION_DAYS' => 365, ];