아파트 전열교환기(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 페이지로 검증, 실사이트는 수동 검증 전용(계정 잠금 방지).
봇 대화창에는 5개 버튼이 상시로 떠 있는 패널이 붙어, 타이핑 없이 조작할 수 있습니다.
[ 환기 켜기 ] [ 환기 끄기 ]
[ 상태 보기 ]
[ 자동화 켜기 ] [ 자동화 끄기 ]
| 버튼 | 명령 | 확인 절차 |
|---|---|---|
| 환기 켜기 | /on |
필요 |
| 환기 끄기 | /off |
필요 |
| 상태 보기 | /status |
없음 |
| 자동화 켜기 | /enable |
없음 |
| 자동화 끄기 | /disable |
필요 |
- 패널은 봇의 모든 명령 답장에 다시 붙으므로 한 번 뜨면 계속 유지됩니다. 클라이언트에서
키보드를 숨겼다면
/menu로 되살립니다. - 기기를 실제로 조작하는 명령은 확인을 한 번 더 받습니다. 버튼은 오조작이 쉬우므로,
확인을 누르기 전에는 브라우저를 띄우지도, 잠금을 잡지도, 상태를 바꾸지도 않습니다. 타이핑으로/on,/off를 보내도 동일하게 확인을 거칩니다. - 패널은 정해진 명령 목록에서만 생성됩니다. 패널을 그리는 것만으로는 브라우저가 뜨거나 기기에 접속하지 않습니다.
- 버튼 탭은 탭한 시각으로 기록되는 일반 메시지로 도착하므로, 오래 전에 표시된 패널을 눌러도 5분 신선도 규칙에 걸리지 않습니다. 반대로 확인 버튼은 그 확인창이 뜬 시각을 기준으로 만료됩니다.
- 인가 규칙(정확한 대화방·사용자·비공개 대화·5분 신선도·허용된 명령)은 버튼 탭과 확인 응답에도 동일하게 적용됩니다. 허용되지 않은 계정은 아무 응답도 받지 못하고 상태도 바뀌지 않습니다.
TypeScript · Node.js (LTS) · Playwright/Chromium · macOS launchd · Telegram Bot API (long polling)
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 아님)에서 순서대로 실행합니다. 모든 명령은 저장소 디렉터리 안에서 실행합니다.
git clone git@github.com:beomeodev/vent-cycle.git
cd vent-cyclenode --version # v24.18.0 이 나와야 함다르면 버전 매니저로 맞춥니다 (fnm use / nvm use — .node-version을 자동으로 읽음).
corepack enable && corepack prepare pnpm@11.13.1 --activate
pnpm --version # 11.13.1pnpm install
pnpm exec playwright install chromium # 빠뜨리면 브라우저 테스트/작업이 전부 실패pnpm build # dist/ 산출물 생성 — launchd 설치가 이 산출물을 요구
pnpm test # 전부 통과해야 함. skipped가 남으면 4번(Chromium)을 안 한 것pnpm exec vent credentials setup # HT 아이디/비밀번호 (입력값은 화면에 표시되지 않음)
pnpm exec vent telegram setup # 텔레그램 봇 토큰 + 허가할 chat_id/user_id비밀값은 macOS Keychain(vent-cycle:*)에만 저장됩니다. .env도, 설정 파일도 없습니다.
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 |
launchctl list | grep com.vent-cycle # 세 에이전트가 보여야 함
pnpm exec vent status # 브라우저 없이 로컬 자동화 상태 표시
pnpm exec vent on # (선택) 수동 원샷 제어로 전체 루프 검증제거: scripts/uninstall-launch-agents.sh (세 에이전트 모두 bootout + 삭제).
자동 로그인으로 세션이 뜨면 ~/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설치 스크립트가 점검·안내합니다 (macOS 전용):
- 절전 금지:
pmset -a sleep 0— 예약이 제때 실행되도록. - 자동 로그인 + 세션 유지: LaunchAgent와 Keychain 접근에 활성 로그인 세션 필요.
- Keychain 잠금 해제: 로그인 Keychain이 세션과 함께 열려 있어야 함.
- Keychain "항상 허용": 첫 접근 시 권한 창에서 한 번 눌러 두면 무인 실행이 막히지 않음.
- Node.js LTS + Playwright Chromium 설치(
npx playwright install chromium).
실패 알림은 영문 에러 코드를 포함합니다(§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 "항상 허용")은 위 "운영 전제조건" 섹션과 설치 스크립트 문서를 참조하세요.