Control
팬 제어, 시스템 모니터링, WakeLock을 제공하는 PHP 기반 단일 관리 패널입니다.
프로젝트 성격
Control은 라즈베리파이/리눅스 호스트의 팬과 시스템 상태를 웹에서 관리하기 위한 내부 운영 도구입니다. 로그인 후 대시보드에서 온도, 팬 RPM, PWM, 저전압/스로틀링, notice를 확인하고 필요한 제어 명령을 실행합니다. 첫 화면은 운영 판단에 필요한 핵심 정보 위주로 구성하고, 상세 차트와 진단성 데이터는 접힘 영역에 둡니다.
상태 갱신은 WebSocket을 우선 사용하고, 연결 실패 시 HTTP fallback으로 전환합니다. 팬 정책 적용과 센서 수집은 백그라운드 작업과 API 호출을 통해 DB에 기록됩니다.
주요 기능
- 팬 모드
auto,manual,off제어 - PWM slider 기반 수동 팬 제어
- 오버레이 설정 모달에서 보안 정책, 팬 자동곡선, 이벤트 판정, 팬 이상감지 파라미터 조정
- CPU 온도, 팬 RPM 핵심 차트
- RP1 온도, 팬 효율, CPU 전력 상세 차트는 접힘 영역에서 확인
- UPS/배터리 잔여 시간 기능은 장치 제거 후 비활성 상태로 두고, 배터리 센서 조회와 장기 학습 계산은 돌리지 않음
- 라즈베리파이 저전압/스로틀링 현재 상태, 복구 중/반복 발생 판정, 부팅 후 이력, 최근 감지 시각, 지속시간, 최근 10분 통계 표시
- System Notice 최근 이력 표시
- process CPU/MEM 후보는 프로세스 상세 접힘 영역에서 표시
/etc/systemd/system/*.service기준 사용자 생성 서비스의 서비스명, 현재 상태, enable 상태, PID, restart 횟수, 최근 journal 로그를 진단 접힘 영역에서 표시- dmesg 로그는 진단 접힘 영역에서 필요할 때 열람
- WakeLock 버튼으로 대시보드 화면 꺼짐 방지
- Reboot 버튼으로 2단계 확인 후 시스템 재부팅 요청
- 기본 브라우저 alert/confirm/prompt 대신 대시보드 디자인에 맞춘 custom dialog 사용
- custom dialog는 Enter/Escape 키 입력을 자체 처리하며, Reboot 확인 흐름은 중복 실행을 차단
- Translate 버튼으로
en,koUI 언어 토글 - Theme 버튼으로
dark,lightUI 테마 토글
주요 API
public/api.php?action=status: 팬, 센서, 시스템, history, notice 상태 조회public/api.php?action=collect: 센서 snapshot 수집 후 팬 정책 적용public/api.php?action=fan: 팬 모드와 PWM 저장/적용public/api.php?action=settings: 설정 가능한 Control 동작값 조회public/api.php?action=settings_save: 설정 모달 값 저장public/api.php?action=settings_reset: 설정 모달 값을 기본값으로 초기화public/api.php?action=dmesg: dmesg 로그 조회public/api.php?action=reboot: 확인 단어와 관리자 암호 재검증 후 sudo reboot 실행
구성
public/index.php: 로그인과 관리 화면public/api.php: 상태 조회와 조작 APIpublic/asset-version.php: 화면과 API 파일 묶음의 현재 hash를 내려주는 JSON 엔드포인트public/assets/asset-reload.js: 열린 브라우저에서 파일 묶음 hash가 달라졌는지 확인하고 새로 여는 스크립트public/assets/app.js: 대시보드 렌더링, WebSocket, 차트, 조작 이벤트public/assets/wakelock.js: Screen Wake Lock API 제어config/config.php: DB, 인증, CSRF, shell 실행 공통 함수apply_policy.php: CLI/cron 팬 정책 적용bin/control_ws.php: WebSocket 서버. 핵심 PHP 파일 변경 감지 시 종료되고 systemd가 재시작해 새 코드를 로드
asset-version.php는 public의 PHP/JS/CSS/manifest/icon 파일과 config의 PHP 파일을 읽어 크기, 수정 시각, sha256 hash를 묶은 버전을 만듭니다. asset-reload.js는 화면 복귀, 포커스 복귀, 30초 간격으로 이 값을 다시 확인하고, 값이 달라지면 브라우저 Cache Storage를 비운 뒤 현재 화면을 다시 엽니다.
데이터/저장소
control_state: 팬 모드와 PWM 상태sensor_logs: 온도, RPM, PWM, load, memory, disk, uptime. 과거 배터리 컬럼은 호환을 위해 유지하지만 UPS 제거 상태에서는 새 배터리값을 기록하지 않습니다./tmp/control-low-voltage-state.json:vcgencmd get_throttled기반 저전압 감지 상태, 최근 지속시간, 최근 episode 이력/tmp/control-throttling-state.json:vcgencmd get_throttled기반 스로틀링 감지 상태, 최근 지속시간, 최근 episode 이력system_notice_state: notice 활성 상태와 기준값system_notice_logs: notice 발생 이력app_settings: 설정 모달에서 저장한 Control 동작값 오버라이드/home/seo/secret/control.php: 앱 비밀번호, DB 설정, 선택적 배터리 설정- UPS/배터리 기능은
/home/seo/secret/control.php의battery.enabled가 명시적으로 true일 때만 동작합니다. 현재 운영 기준은 UPS 제거 상태이므로 기본값은 false입니다.
UPS/배터리 상태
UPS가 과부하 상황에서 전압 하강과 강제 종료를 반복해 현재 운영 구성에서는 제거되어 있습니다. 이에 따라 Control은 기본적으로 MAX17048 배터리 센서를 읽지 않고, 배터리 SOC/전압 차트와 잔여 시간 UI도 표시하지 않습니다.
API 응답의 battery.enabled는 false, battery.removed는 true로 내려가며 battery.remaining.display는 UPS 제거됨으로 표시됩니다. 과거에 저장된 sensor_logs의 배터리 컬럼은 DB 호환을 위해 남겨 두지만 새 snapshot에서는 배터리값을 추가하지 않습니다.
나중에 UPS 구성을 다시 붙일 경우 /home/seo/secret/control.php의 battery.enabled를 true로 두면 기존 배터리 측정/학습 코드가 다시 사용됩니다.
설정 모달
상단 설정 버튼은 전체 화면 오버레이 모달을 엽니다. 설정값은 app_settings 테이블에 저장되며, 저장 직후 다음 snapshot부터 반영됩니다. 민감정보가 들어 있는 /home/seo/secret/control.php는 직접 수정하지 않고 DB 오버라이드만 사용합니다.
설정 가능한 항목은 다음 범위입니다.
- 팬 자동 제어: 팬 시작 온도, 최대 온도, 즉시 최대 PWM 온도, RP1 온도 반영 비율, 팬 판단 온도 여유, 자동 상승/하강 PWM step
- 보안 정책: 자동 로그인 유지 기간, remember 쿠키 Secure/SameSite, CSRF 길이, remember 토큰 길이, User-Agent/IP 접두사 검증, 로그인 실패 잠금, 재부팅 허용 여부, 재부팅 확인 문구, 명령 timeout
- 화면/진단 표시: 프로세스 후보 수, 사용자 서비스 로그 줄 수, 사용자 서비스 캐시 시간, 팬 이상 이력 수
- 저전압/스로틀링: 최근 판정창, 복구 중 유지 시간, 반복 발생 기준 횟수
- 팬 이상감지: 기준 표본 수, 최신 표본 제외 수, RPM/온도 이상 감지 차이, RPM/온도 복구 차이, RPM 감시 시작 PWM, alert 유지 중 반복 기록, 반복 기록 간격, 하강 변화 원인 후보 표시 여부
- UPS/배터리 설정: 현재 UPS 제거 상태에서는 설정 모달에서 숨겨지고,
battery.enabled=true일 때만 배터리 예측 항목을 노출합니다.
설정 모달이 열려 있는 동안 1초 상태 갱신은 폼 DOM을 다시 만들지 않습니다. 입력 중인 값은 저장, 기본값 초기화, 닫기 전까지 유지되며, 백그라운드 snapshot은 내부 설정 payload만 갱신합니다. 단순 boolean 설정은 켜기/끄기 세그먼트로 표시합니다.
갱신 주기
- WebSocket 상태 갱신: 1초마다
- WebSocket 최초 연결: 즉시 1회 상태 전송
- WebSocket 끊김 시 HTTP fallback 상태 갱신: 2초마다
- 탭 복귀: 즉시 1회 상태 갱신
- UPS/배터리 잔여 시간: 현재 운영 구성에서는 표시하지 않음
- dmesg: 진단 패널에서 열었을 때만 1초마다 갱신, 닫으면 중지
- WebSocket 소스 변경 감지: 15초마다 확인 후 재시작
- 팬 슬라이더 자동 적용 debounce: 약 450ms
System Notice 구조
Control은 급격한 팬/온도 변화를 외부 푸시로 보내지 않고 system_notice_logs에 로컬 이력으로 저장합니다. 대시보드는 최근 이력을 진단 영역에서 보여주며, 배터리 SOC 낮음 같은 모바일 푸시 발송은 더 이상 수행하지 않습니다.
시스템 알림 조건
System Notice는 다음 조건으로 동작합니다.
- System Notice 시작: 팬 모드가
off가 아닐 때, 최근 안정 기준선 대비 팬 RPM 차이가팬 RPM 이상 감지 차이이상이거나 온도 차이가온도 이상 감지 차이이상이면 alert로 진입합니다. RPM 감시는 현재 PWM 또는 기준 PWM이RPM 감시 시작 PWM이상일 때만 의미 있는 변화로 봅니다. - System Notice 기준선: normal 상태에서는 최근 sensor history에서 최신 몇 개 표본을 제외하고 절사 평균으로 기준선을 계속 갱신합니다. alert로 들어가면 진입 당시 온도/RPM/PWM 기준선을 고정해, alert 중 기준선이 문제 상태를 따라가며 사라지는 일을 막습니다.
- System Notice 복구: 고정 기준선 또는 최신 rolling 기준선 대비 RPM/온도 차이가 각각 복구 차이 이하로 내려가면 normal로 돌아갑니다. 팬 모드가
off이면 fan off 기준선을 별도로 고정하고 System Notice alert를 만들지 않습니다. - System Notice 기록: alert 신규 진입 시
system_notice_logs에 기록합니다.Alert 유지 중 반복 기록을 켜면 유지 중에도 설정한 반복 기록 간격마다 다시 기록할 수 있습니다. - 저전압/스로틀링 표시:
vcgencmd get_throttled의 현재 비트와 부팅 후 이력 비트를 읽어 현재 상태, 최근 감지, 지속시간, 최근 10분 통계를 표시합니다. 이 값은 System Notice와 별개이며, 팬/온도 alert 조건에는 직접 섞지 않습니다.
처리 흐름
- 화면 진입 시 로그인 세션과 remember token을 확인합니다.
- Rhymix 사이트 관리자 세션 또는
/custom/common관리자 인증 쿠키가 확인되면 Control 로그인 화면을 생략하고 내부 세션을 발급합니다. - 대시보드는
statussnapshot을 렌더링합니다. - WebSocket 연결이 성공하면
status메시지로 갱신하고, 실패하면 HTTP fallback을 사용합니다. - 팬 조작은 상태 저장, 정책 적용, 로그 저장 순서로 처리합니다.
- WakeLock 버튼은 활성 상태를 초록색 버튼으로 표시합니다.
- Reboot 버튼은 설정된 확인 문구와 관리자 암호 재입력을 모두 통과한 뒤 서버 API로 재부팅을 요청합니다.
- 시스템 상태는
vcgencmd get_throttled를 읽어 현재 저전압/스로틀링 여부와 부팅 후 이력, 최근 감지 시각, 현재/최근 지속시간을 표시합니다. - 저전압/스로틀링 상태 파일에는 최근 episode 시작/종료를 보관하고, 최근 10분 발생 횟수, 감지 누적시간, 감지 비율을 계산합니다.
- UPS/배터리 장치가 제거되어 배터리 센서 조회와 잔여 시간 학습 계산은 기본적으로 돌리지 않습니다.
- System Notice 조건이 맞으면 Control이
system_notice_logs에 이력을 저장합니다. - 대시보드 첫 화면에는 팬/전원/스로틀링/핵심 차트를 우선 배치하고, 상세 차트, 프로세스 후보, 사용자 서비스, dmesg는 접힘 영역으로 분리합니다.
팬 판단 온도
화면의 CPU 온도 표시는 기존처럼 CPU 센서 값을 보여줍니다. 자동 팬 제어는 CPU만 보지 않고 RP1 온도도 함께 반영한 fan_control_temp() 값을 사용합니다.
계산은 CPU 온도와 RP1 온도를 설정된 RP1 온도 반영 비율로 섞은 뒤 팬 판단 온도 여유를 더하고, 이 값과 CPU 온도 중 더 높은 값을 팬 판단 온도로 삼습니다. 기본값은 RP1 35%, 여유 1.5도입니다. RP1 값이 없으면 CPU 온도만 사용합니다.
이 구조는 팬 하나가 CPU와 RP1 주변 열을 같이 빼는 현실을 반영하되, 화면의 CPU 표기와 차트 의미는 흐리지 않도록 팬 제어 내부 값만 따로 둡니다. API 응답에는 fan.control_temp_c와 system.fan_control_temp_c가 함께 내려갑니다.
주요 함수/모듈
collect_snapshot(): 센서와 fan 상태 snapshot 생성custom_systemd_services():/etc/systemd/system/*.service단위를 조회해 사용자 생성 서비스 상태와 최근 journal 로그를 구성systemd_service_logs(): 서비스별 최근 journal 로그 조회read_throttled_flags():vcgencmd get_throttled를 직접 조회하고, 권한 문제로 실패하면 sudo 경유 조회를 시도throttled_event_status(): 저전압/스로틀링 episode 이력, 복구 중/반복 발생 판정, 최근 10분 통계를 계산throttled_statuses(): 라즈베리파이 throttled flag를 읽고 저전압/스로틀링 감지 이력을 상태 파일로 추적apply_fan_policy(): 팬 목표값 계산과 적용fan_control_temp(): CPU/RP1 혼합 팬 판단 온도 계산json_out(): API JSON 응답 표준화battery_status():CONTROL_BATTERY_ENABLED가 true일 때만 배터리 센서를 읽고, 현재 기본 구성에서는 제거 상태를 반환battery_trend_history(): 배터리 기능을 다시 켤 때 최근 24시간 배터리 기록을 1분 단위로 집계battery_profile_history(): 배터리 기능을 다시 켤 때 최대 45일 배터리 기록을 약 5분 간격으로 샘플링하고 파일 캐시로 보관weighted_linear_regression(),numeric_mad(): SOC 기울기 계산과 회귀 잔차 튐 제거battery_regression_trend_candidate(): 1분 SOC 집계를 강건 회귀로 분석해 정밀 방전 속도 후보 계산battery_voltage_trend_candidate(): 전압 기울기와 동적 하한 전압으로 잔여 시간 후보 계산battery_energy_profile_candidate(): 장기 기록에서 SOC 1%당 실제 사용 Wh를 학습해 잔여 시간 후보 계산battery_capacity_power_candidate(): 설정 배터리 용량과 최근 전력으로 용량 기반 후보 계산battery_runtime_cap_seconds(),battery_cap_candidate(),battery_cap_result(): SOC 비례 현실 상한과 상한 초과 후보 감쇠 적용refine_battery_candidates(): 후보 방전 속도의 중앙값에서 크게 벗어난 값을 제외하거나 가중치 축소battery_learned_profile_candidate(): 누적 기록에서 SOC/부하 구간별 장기 방전 프로파일 후보 계산battery_remaining_estimate(): SOC 다중 시간창 방전 추세, 장기 학습 프로파일, 현재 부하 보정으로 잔여 시간 계산battery_power_fallback_estimate(): SOC 추세가 부족할 때 배터리 용량과 최근 CPU 전력 평균으로 잔여 시간 대체 계산sync_current_battery_remaining_history(): 잔여 시간 차트 마지막 지점을 현재 snapshot 잔여 시간과 동기화batteryRemainingTitle(),showBatteryTooltip(),updateBatteryTooltip(): 잔여 시간 계산 근거 커스텀 팝오버 표시와 실시간 갱신setting_definitions(),settings_payload(),save_settings(),reset_settings(): 설정 모달 항목 정의, 조회, 저장, 초기화assets/wakelock.js: WakeLock 버튼 상태와 Screen Wake Lock API 제어customAlert(),customConfirm(),customPrompt(): 대시보드 공통 확인/입력 dialogcontrolLang,controlTheme: 재접속 후에도 유지되는 언어/테마 localStorage keyCONTROL_BATTERY_ENABLED: UPS/배터리 센서와 잔여시간 학습 계산 사용 여부BATTERY_CELL_CAPACITY_MAH,BATTERY_PARALLEL_CELLS,BATTERY_CAPACITY_MAH,BATTERY_NOMINAL_VOLTAGE,BATTERY_CAPACITY_WH: 배터리 기능을 다시 켤 때 쓰는 병렬 배터리팩 용량과 잔여시간 계산 기준
보안
- 로그인 세션과 CSRF token을 사용합니다.
- Rhymix 사이트 관리자 세션 또는
/custom/common관리자 인증 쿠키가 확인되면 Control 내부 로그인 세션을 발급합니다. - Control 로그아웃은 Control remember token과 세션을 지우고,
/custom/common공통 인증 쿠키도 함께 만료합니다. - POST 조작 API는 CSRF 검증을 통과해야 합니다.
- sysfs 쓰기와 systemctl 실행 권한은 sudoers 범위로 제한해야 합니다.
- 재부팅 API는 CSRF, 로그인 세션, 설정된 확인 문구, 관리자 암호 재검증을 모두 요구합니다.
- 로그인 화면은 CSRF 검사를 수행하고, 설정한 실패 횟수/집계 시간/잠금 시간에 따라 세션 단위 잠금을 적용합니다.
- Remember login은 설정에 따라 Secure/SameSite, 토큰 길이, 만료 기간, User-Agent 고정, IPv4 접두사 고정을 조정할 수 있습니다.
- 앱 비밀번호와 DB/선택적 배터리 설정은 저장소 밖 secret 파일로 관리합니다.
운영 체크포인트
- 센서 수집 주기와 DB 증가량을 확인합니다.
- UPS/배터리를 다시 붙일 때는
/home/seo/secret/control.php의battery.enabled와 용량값을 먼저 확인합니다. - 하드웨어 또는 OS 변경 후 fan sysfs 경로를 확인합니다.
- WebSocket은 장기 실행 프로세스이므로
public/api.php,config/config.php,bin/control_ws.php변경을 감지하면 15초 안에 종료되고control-websocket.service가 새 프로세스로 재시작합니다. - WebSocket 장기 실행 중 DB 연결이 끊길 수 있으므로 reconnect 로그를 확인합니다.
- SmartThings 재로드 자동화는 서울 전력분전반과 목포 누전차단기 전력 센서의 값이 10초 이상 변하지 않으면 통합을 재로드합니다.
- Reboot API 사용 전 웹 서버 실행 계정의 sudoers에
/usr/sbin/reboot비밀번호 없는 실행 권한이 제한적으로 설정되어 있는지 확인합니다. - 저전압/스로틀링이
N/A로 보이면 웹 서버 실행 계정의/dev/vcio접근 권한과/usr/bin/vcgencmd get_throttledsudoers 허용 여부를 확인합니다. - 언어/테마 표시가 예상과 다르면 브라우저 localStorage의
controlLang,controlTheme값을 확인합니다. /mnt/synology-web는 원격 Synology NFS 마운트이며sec=sys숫자 UID/GID 매핑을 사용합니다. 로컬seo의 기본 UID/GID만으로는/mnt/synology-web/log,/mnt/synology-web/report,/mnt/synology-web/config에 쓸 수 없고,synologywebprimary group으로 실행해야 합니다./home/seo/log.sh는 시작 시sg synologyweb으로 재실행하도록 구성합니다./mnt/synology-docker/homeassistant/config는 현재 일반seo와synologywebprimary group 모두 쓰기 가능합니다.