2026-07-20 00:42:12 +09:00
2026-07-20 00:42:12 +09:00
2026-07-20 00:42:12 +09:00
2026-07-20 00:42:12 +09:00
2026-06-07 00:33:58 +09:00
2026-06-21 07:30:39 +09:00
2026-06-21 07:30:39 +09:00
2026-07-20 00:42:12 +09:00

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, ko UI 언어 토글
  • Theme 버튼으로 dark, light UI 테마 토글

주요 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: 상태 조회와 조작 API
  • public/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.phppublic의 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.phpbattery.enabled가 명시적으로 true일 때만 동작합니다. 현재 운영 기준은 UPS 제거 상태이므로 기본값은 false입니다.

UPS/배터리 상태

UPS가 과부하 상황에서 전압 하강과 강제 종료를 반복해 현재 운영 구성에서는 제거되어 있습니다. 이에 따라 Control은 기본적으로 MAX17048 배터리 센서를 읽지 않고, 배터리 SOC/전압 차트와 잔여 시간 UI도 표시하지 않습니다.

API 응답의 battery.enabled는 false, battery.removed는 true로 내려가며 battery.remaining.displayUPS 제거됨으로 표시됩니다. 과거에 저장된 sensor_logs의 배터리 컬럼은 DB 호환을 위해 남겨 두지만 새 snapshot에서는 배터리값을 추가하지 않습니다.

나중에 UPS 구성을 다시 붙일 경우 /home/seo/secret/control.phpbattery.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 조건에는 직접 섞지 않습니다.

처리 흐름

  1. 화면 진입 시 로그인 세션과 remember token을 확인합니다.
  2. Rhymix 사이트 관리자 세션 또는 /custom/common 관리자 인증 쿠키가 확인되면 Control 로그인 화면을 생략하고 내부 세션을 발급합니다.
  3. 대시보드는 status snapshot을 렌더링합니다.
  4. WebSocket 연결이 성공하면 status 메시지로 갱신하고, 실패하면 HTTP fallback을 사용합니다.
  5. 팬 조작은 상태 저장, 정책 적용, 로그 저장 순서로 처리합니다.
  6. WakeLock 버튼은 활성 상태를 초록색 버튼으로 표시합니다.
  7. Reboot 버튼은 설정된 확인 문구와 관리자 암호 재입력을 모두 통과한 뒤 서버 API로 재부팅을 요청합니다.
  8. 시스템 상태는 vcgencmd get_throttled를 읽어 현재 저전압/스로틀링 여부와 부팅 후 이력, 최근 감지 시각, 현재/최근 지속시간을 표시합니다.
  9. 저전압/스로틀링 상태 파일에는 최근 episode 시작/종료를 보관하고, 최근 10분 발생 횟수, 감지 누적시간, 감지 비율을 계산합니다.
  10. UPS/배터리 장치가 제거되어 배터리 센서 조회와 잔여 시간 학습 계산은 기본적으로 돌리지 않습니다.
  11. System Notice 조건이 맞으면 Control이 system_notice_logs에 이력을 저장합니다.
  12. 대시보드 첫 화면에는 팬/전원/스로틀링/핵심 차트를 우선 배치하고, 상세 차트, 프로세스 후보, 사용자 서비스, 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_csystem.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(): 대시보드 공통 확인/입력 dialog
  • controlLang, controlTheme: 재접속 후에도 유지되는 언어/테마 localStorage key
  • CONTROL_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.phpbattery.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_throttled sudoers 허용 여부를 확인합니다.
  • 언어/테마 표시가 예상과 다르면 브라우저 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에 쓸 수 없고, synologyweb primary group으로 실행해야 합니다. /home/seo/log.sh는 시작 시 sg synologyweb으로 재실행하도록 구성합니다.
  • /mnt/synology-docker/homeassistant/config는 현재 일반 seosynologyweb primary group 모두 쓰기 가능합니다.
S
Description
Fan, system, WiFi, WakeLock, and Web Push control panel
Readme 5.4 MiB
Languages
PHP 65.4%
JavaScript 33%
CSS 1.6%