Skip to content

Repository files navigation

Vent Cycle

아파트 전열교환기(ERV)를 HT Home Service 웹사이트를 통해 자동 제어하는 macOS 로컬 자동화 시스템.

Mac mini의 launchd가 하루 48회(1시간 주기, 20분 ON / 40분 OFF) 원샷 브라우저 작업을 깨우고, 매번 새 Chromium으로 로그인 → 버튼 텍스트로 상태 판독 → 필요할 때만 클릭 → 반대 버튼으로 검증 → 완전 종료합니다. 상주 프로세스는 브라우저 없는 Telegram 폴링 에이전트 하나뿐이며, 알림은 실패에만 보냅니다.

대상 서비스: 한국 아파트에서 널리 쓰이는 HT Home Service(www2.hthomeservice.com)의 환기 제어를 자동화합니다. 같은 서비스를 쓰는 환경이면 그대로 활용할 수 있고, 아니더라도 원샷 브라우저 · launchd 예약 · Playwright 제어 구조의 참고 구현으로 볼 수 있습니다.

핵심 원칙

  • 원샷 브라우저: 시도마다 새 Chromium + 비영속 컨텍스트 + 새 로그인 + 완전 정리. 상주 브라우저·프로필 재사용 금지.
  • 블라인드 토글 금지: 상태를 먼저 읽고, 목표와 다를 때만 idempotent ensureVentilationOn/Off 실행. 상태 불명이면 클릭하지 않음.
  • 실패만 알림: 정상 성공은 무음. 동일 장애 반복은 억제 후 요약(기본 6시간 창), 복구 시 1회 알림. 메시지는 한국어, 에러 코드는 영문.
  • 비밀은 Keychain에만: vent-cycle:* 서비스명. 소스/.env/로그/스크린샷/Git 어디에도 없음.
  • 실사이트 자동 테스트 금지: 사이트 플로우는 로컬 fixture 페이지로 검증, 실사이트는 수동 검증 전용(계정 잠금 방지).

Telegram 조작

봇 대화창에는 5개 버튼이 상시로 떠 있는 패널이 붙어, 타이핑 없이 조작할 수 있습니다.

[ 환기 켜기 ] [ 환기 끄기 ]
[      상태 보기       ]
[ 자동화 켜기 ] [ 자동화 끄기 ]
버튼 명령 확인 절차
환기 켜기 /on 필요
환기 끄기 /off 필요
상태 보기 /status 없음
자동화 켜기 /enable 없음
자동화 끄기 /disable 필요
  • 패널은 봇의 모든 명령 답장에 다시 붙으므로 한 번 뜨면 계속 유지됩니다. 클라이언트에서 키보드를 숨겼다면 /menu로 되살립니다.
  • 기기를 실제로 조작하는 명령은 확인을 한 번 더 받습니다. 버튼은 오조작이 쉬우므로, 확인을 누르기 전에는 브라우저를 띄우지도, 잠금을 잡지도, 상태를 바꾸지도 않습니다. 타이핑으로 /on, /off를 보내도 동일하게 확인을 거칩니다.
  • 패널은 정해진 명령 목록에서만 생성됩니다. 패널을 그리는 것만으로는 브라우저가 뜨거나 기기에 접속하지 않습니다.
  • 버튼 탭은 탭한 시각으로 기록되는 일반 메시지로 도착하므로, 오래 전에 표시된 패널을 눌러도 5분 신선도 규칙에 걸리지 않습니다. 반대로 확인 버튼은 그 확인창이 뜬 시각을 기준으로 만료됩니다.
  • 인가 규칙(정확한 대화방·사용자·비공개 대화·5분 신선도·허용된 명령)은 버튼 탭과 확인 응답에도 동일하게 적용됩니다. 허용되지 않은 계정은 아무 응답도 받지 못하고 상태도 바뀌지 않습니다.

스택

TypeScript · Node.js (LTS) · Playwright/Chromium · macOS launchd · Telegram Bot API (long polling)

CLI

vent는 이 저장소의 pnpm 패키지 명령입니다. 저장소 디렉터리에서 pnpm exec vent <명령>으로 실행합니다 (아래 예시는 vent로 축약 — 편하게 쓰려면 alias vent="pnpm exec vent").

vent apply                  # launchd 진입점 — 현재 시각에서 목표 전환을 역산 (--scheduled-at는 테스트 전용)
vent status                 # 브라우저 없이 로컬 자동화 상태 표시
vent on / vent off          # 수동 원샷 제어
vent automation enable      # 자동화 켜기 (다음 예약부터)
vent automation disable     # 자동화 끄기 + 환기 끄기 시도
vent credentials setup      # Keychain에 HT 자격증명 저장
vent telegram setup         # Keychain에 텔레그램 봇 토큰 저장
vent telegram run           # 텔레그램 제어 에이전트(장기 폴링) 실행
vent maintenance rotate     # 로그 7일 / 실패 스크린샷 30장 보존 정책 적용

설치 및 설정

Mac mini의 대상 사용자 계정(자동화를 돌릴 계정, root 아님)에서 순서대로 실행합니다. 모든 명령은 저장소 디렉터리 안에서 실행합니다.

1. 코드 받기

git clone git@github.com:beomeodev/vent-cycle.git
cd vent-cycle

2. Node 버전 맞추기 — .node-version에 고정된 24.18.0

node --version          # v24.18.0 이 나와야 함

다르면 버전 매니저로 맞춥니다 (fnm use / nvm use.node-version을 자동으로 읽음).

3. pnpm 준비 — package.json에 11.13.1로 고정

corepack enable && corepack prepare pnpm@11.13.1 --activate
pnpm --version          # 11.13.1

4. 의존성 설치 + Chromium 내려받기

pnpm install
pnpm exec playwright install chromium   # 빠뜨리면 브라우저 테스트/작업이 전부 실패

5. 빌드 + 테스트

pnpm build              # dist/ 산출물 생성 — launchd 설치가 이 산출물을 요구
pnpm test               # 전부 통과해야 함. skipped가 남으면 4번(Chromium)을 안 한 것

6. 비밀값을 Keychain에 저장

pnpm exec vent credentials setup   # HT 아이디/비밀번호 (입력값은 화면에 표시되지 않음)
pnpm exec vent telegram setup      # 텔레그램 봇 토큰 + 허가할 chat_id/user_id

비밀값은 macOS Keychain(vent-cycle:*)에만 저장됩니다. .env도, 설정 파일도 없습니다.

7. LaunchAgent 3종 등록 (root 불필요)

scripts/install-launch-agents.sh

스크립트가 §17.4 전제조건(절전, 자동 로그인, Keychain, Node, Chromium)을 점검한 뒤, plist 3개를 이 기계의 node·CLI 절대경로로 생성해 사용자 도메인(gui/$(id -u))에 등록합니다 (launchd는 셸 PATH를 물려받지 않으므로 절대경로가 필수).

에이전트 plist 역할
스케줄 com.vent-cycle.schedule 하루 48회 vent apply (활성 스케줄에서 재생성)
텔레그램 com.vent-cycle.telegram 상주 폴링, 실패 시 재시작
보관 정리 com.vent-cycle.retention 매일 03:30 vent maintenance rotate

8. 동작 확인

launchctl list | grep com.vent-cycle   # 세 에이전트가 보여야 함
pnpm exec vent status                  # 브라우저 없이 로컬 자동화 상태 표시
pnpm exec vent on                      # (선택) 수동 원샷 제어로 전체 루프 검증

제거: scripts/uninstall-launch-agents.sh (세 에이전트 모두 bootout + 삭제).

9. 재시작 후 (이미 세팅을 마친 경우)

자동 로그인으로 세션이 뜨면 ~/Library/LaunchAgents의 세 에이전트는 재부팅 시 자동 로드됩니다. 따라서 세팅을 마친 기계는 재시작 후 상태만 확인하면 되고, 비어 있을 때만 재등록합니다.

# Mac mini 재부팅 후 — 상태 확인 (자동 로그인 세션이 활성인 상태에서)
launchctl list | grep com.vent-cycle
#   기대: 세 줄 모두 둘째 칸(마지막 종료 코드)이 0
#     com.vent-cycle.telegram    <PID>  0   ← 상주 실행 중
#     com.vent-cycle.schedule    -      0   ← 다음 정시 대기
#     com.vent-cycle.retention   -      0   ← 매일 03:30 대기
#
# 비어 있거나 종료 코드가 0이 아니면, 프로젝트 폴더에서 재등록(멱등):
#   scripts/install-launch-agents.sh

운영 전제조건 (PRD §17.4)

설치 스크립트가 점검·안내합니다 (macOS 전용):

  • 절전 금지: pmset -a sleep 0 — 예약이 제때 실행되도록.
  • 자동 로그인 + 세션 유지: LaunchAgent와 Keychain 접근에 활성 로그인 세션 필요.
  • Keychain 잠금 해제: 로그인 Keychain이 세션과 함께 열려 있어야 함.
  • Keychain "항상 허용": 첫 접근 시 권한 창에서 한 번 눌러 두면 무인 실행이 막히지 않음.
  • Node.js LTS + Playwright Chromium 설치(npx playwright install chromium).

장애 대응 (Failure Playbook)

실패 알림은 영문 에러 코드를 포함합니다(§14 에러 분류). 주요 코드별 대응:

에러 코드 의미 권장 조치
CREDENTIALS_MISSING / CREDENTIALS_UNREADABLE Keychain 자격증명 없음/읽기 실패 vent credentials setup 재실행, "항상 허용" 확인
LOGIN_REJECTED 로그인 거부(비밀번호 변경 등) 비밀번호 확인 후 vent credentials setup 재실행 (자동 재시도 안 함 — 계정 잠금 방지)
MFA_OR_CAPTCHA_REQUIRED / ACCOUNT_LOCKED 추가 인증/계정 제한 수동으로 사이트 로그인해 해제, 자동화는 우회하지 않음
DEVICE_STATE_AMBIGUOUS 상태 버튼이 모호하게 판독됨(둘 이상 보임) 월패드/HT 앱에서 환기 상태 확인, 사이트 DOM 변경 여부 점검
CONTROL_TIMEOUT / CONTROL_STUCK 클릭 후 상태 미전환 장치/게이트웨이 지연 가능, 다음 예약에서 재시도됨
SITE_UNREACHABLE / NAVIGATION_TIMEOUT 네트워크/사이트 접속 실패 인터넷 연결·사이트 상태 확인
SKIPPED_STALE_TRIGGER 예약이 3분 넘게 지연되어 건너뜀 조치 불필요(설계된 동작, 알림 없음)
TASK_ALREADY_RUNNING 다른 작업이 락 보유 조치 불필요(정상 경합)

동일 코드 반복 실패는 억제되고 6시간마다 요약, 복구 시 1회 알림이 옵니다.

문서

문서 경로
시스템 맵 (구조 · 불변조건 · 검증 명령) docs/SYSTEM_MAP.md
HT 사이트 구조 분석 docs/site-discovery.md
인수 기준 (수동 프로덕션 테스트) docs/acceptance/feature-008-acceptance.md

운영 전제조건(절전 금지, 자동 로그인, Keychain "항상 허용")은 위 "운영 전제조건" 섹션과 설치 스크립트 문서를 참조하세요.

About

macOS-local automation for the Korean HT Home Service apartment ventilation — one-shot Playwright, launchd scheduling, Telegram control

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages