# Homebridge Samsung AC (구형 모델 / Legacy Model)

구형 삼성 에어컨(TLSv1 통신 방식)을 Apple HomeKit에 연동하기 위한 Homebridge **플랫폼** 플러그인입니다.

이 플러그인은 최신 Node.js(v17 이상) 환경에서 발생하는 TLS 호환성 문제를 해결하기 위한 패치를 포함하고 있습니다. 또한, **인증서가 플러그인에 내장**되어 있어 사용자는 더 이상 `.pem` 파일을 직접 구하거나 경로를 설정할 필요 없이, 오직 **IP 주소와 토큰만으로** 플러그인을 설정할 수 있습니다.

**플랫폼(Platform)** 방식으로 전환되어, 불안정한 에어컨 장치를 **자식 브릿지(Child Bridge)**에 격리하여 홈브릿지 전체의 안정성을 확보할 수 있습니다.

## 주요 기능 ✨

* **구형 삼성 에어컨 지원**: TLSv1 통신 프로토콜을 사용하는 모델 지원
* **최신 Homebridge 호환**: Node.js v17, v18, v22 등 최신 버전에서 발생하는 모든 TLS/HTTP 오류 해결
* **안정성 확보**: **자식 브릿지** 지원으로, 에어컨의 응답 없음 문제가 다른 액세서리에 영향을 주지 않도록 격리 가능
* **인증서 내장**: 별도의 `.pem` 파일 설정이 필요 없어 설정이 매우 간편함
* **UI 설정 지원**: Homebridge UI를 통해 모든 설정을 간편하게 구성 가능
* **안정적인 통신**: 상태 캐싱, 자동 재시도, 주기적인 상태 폴링 기능 포함

## 사전 준비 checklist

* Homebridge 최신 버전 (UI 환경 권장)
* 에어컨의 고정 IP 주소
* Python 3 및 OpenSSL (Homebridge가 설치된 환경이라면 대부분 이미 설치되어 있습니다)
* **공유기 관리자 페이지 접근 권한** (DNS 설정 변경을 위해 필요)

## 설치 💻

Homebridge UI의 '플러그인' 탭에서 `homebridge-samsung-ac`을 검색하여 설치합니다.

---

## 🔑 에어컨 토큰(Token) 추출 방법 (필수 절차)

이 플러그인을 사용하려면 에어컨의 고유 인증 토큰이 필요합니다. 토큰은 **DNS 스푸핑(Spoofing)**이라는 기법을 사용하여, 에어컨이 삼성 클라우드 서버(`api.smartthings.com`)와 통신하는 내용을 중간에서 가로채어 추출합니다.

### **1단계: 가짜 서버 스크립트 준비**

1.  아래의 Python 코드를 복사하여 컴퓨터(또는 Homebridge가 설치된 NAS/라즈베리파이)에 `fake_server.py` 라는 이름으로 저장합니다. 이 스크립트는 에어컨의 통신을 받아낼 가짜 서버 역할을 합니다.

    ```python
    #!/usr/bin/env python3
    import ssl
    import socket
    import os
    import threading

    # 설정
    LISTEN_IP = '0.0.0.0' # 모든 IP에서 들어오는 연결을 수신
    HTTPS_PORT = 443
    CERT_FILE = 'temp_server_cert.pem'
    KEY_FILE = 'temp_server_key.pem'

    def generate_self_signed_cert(cert_file, key_file):
        """임시 SSL 서버 인증서 생성"""
        if not (os.path.exists(cert_file) and os.path.exists(key_file)):
            print(f"임시 서버 인증서 '{cert_file}' 및 '{key_file}' 생성 중...")
            # openssl이 설치되어 있어야 함
            subj = "/CN=api.smartthings.com"
            os.system(f'openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 -keyout {key_file} -out {cert_file} -subj "{subj}"')
        print("임시 서버 인증서 준비 완료.")

    def handle_client(conn, addr):
        """클라이언트 연결 처리 및 데이터 출력"""
        print(f"\n>>> [연결 수립] From: {addr}")
        try:
            while True:
                data = conn.recv(8192)
                if not data:
                    break
                
                decoded_data = data.decode('utf-8', errors='ignore')
                print("\n" + "="*20 + " 데이터 수신 " + "="*20)
                print(decoded_data)
                
                # 'Authorization' 헤더에서 토큰 찾기
                for line in decoded_data.splitlines():
                    if 'authorization' in line.lower():
                        print("\n" + "*"*20 + " 🎉 토큰 발견! 🎉 " + "*"*20)
                        token = line.split(' ')[-1]
                        print(f"추출된 토큰: {token}")
                        print("*"*56)
                        print("이 토큰을 복사하여 Homebridge 설정에 사용하세요.")
                        
                # 에어컨에 정상적인 HTTP 응답을 보내줘야 연결 절차가 완료됨
                conn.sendall(b'HTTP/1.1 200 OK\r\n\r\n')

        except Exception as e:
            print(f"[오류] 클라이언트 처리 중 오류: {e}")
        finally:
            print(f"<<< [연결 종료] From: {addr}")
            conn.close()

    def main():
        """가짜 서버를 실행하여 토큰을 수신하고 출력"""
        generate_self_signed_cert(CERT_FILE, KEY_FILE)
        context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
        context.load_cert_chain(certfile=CERT_FILE, keyfile=KEY_FILE)
        
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            sock.bind((LISTEN_IP, HTTPS_PORT))
            sock.listen(5)
            
            print("\n" + "="*50)
            print(f"가짜 삼성 클라우드 서버가 시작되었습니다. (포트: {HTTPS_PORT})")
            print("이제 에어컨을 Wi-Fi 설정 모드로 변경하고, SmartThings 앱으로 연결을 시도하세요.")
            print("="*50)

            while True:
                conn, addr = sock.accept()
                threading.Thread(target=handle_client, args=(conn, addr)).start()

    if __name__ == '__main__':
        try:
            main()
        except PermissionError:
            print("\n[오류] 443 포트를 사용하려면 root 권한이 필요합니다. 'sudo python3 fake_server.py'로 실행해주세요.")
        except KeyboardInterrupt:
            print("\n서버를 종료합니다.")
        finally:
            # 종료 시 임시 인증서 파일 삭제
            if os.path.exists(CERT_FILE): os.remove(CERT_FILE)
            if os.path.exists(KEY_FILE): os.remove(KEY_FILE)
    ```

### **2단계: DNS 스푸핑 설정 (가장 중요!)**

1.  **스크립트 실행 컴퓨터의 IP 주소 확인:** `fake_server.py`를 실행할 컴퓨터(NAS, 라즈베리파이 등)의 **내부 IP 주소를 확인**합니다. (예: `192.168.1.10`)
2.  **공유기 관리자 페이지 접속:** 웹 브라우저에서 공유기 관리자 페이지(보통 `192.168.0.1` 또는 `192.168.1.1`)에 접속합니다.
3.  **DNS 설정 메뉴 찾기:** '고급 설정'의 'LAN 설정' 또는 'DNS' 관련 메뉴에서 **'정적 DNS(Static DNS)', 'DNS 호스트 이름(Hostname)'** 과 같은 기능을 찾습니다. (공유기 제조사마다 메뉴 이름이 다를 수 있습니다)
4.  아래 내용을 입력하고 저장/적용합니다. 이 설정은 `api.smartthings.com`으로 가야 할 에어컨의 요청을 우리 컴퓨터로 오게 만듭니다.
    * **호스트 이름 / 도메인 이름:** `api.smartthings.com`
    * **IP 주소:** 위에서 확인한 **스크립트 실행 컴퓨터의 IP 주소** (예: `192.168.1.10`)

    ![공유기 DNS 설정 예시](https://i.imgur.com/u5jJmYQ.png)

### **3단계: 토큰 추출 실행**

1.  **가짜 서버 실행:** 터미널(PuTTY 등)에서 `fake_server.py` 파일이 있는 폴더로 이동 후, **root 권한**으로 스크립트를 실행합니다. (443번 포트 사용을 위해 root 권한 필요)
    ```bash
    sudo python3 fake_server.py
    ```
2.  **에어컨 연결 시도:** 에어컨을 **Wi-Fi 설정 모드**로 변경하고, **SmartThings 앱**을 사용하여 네트워크 연결 절차를 진행합니다.
3.  **토큰 확인:** 에어컨이 Wi-Fi에 연결된 후 삼성 서버와 통신을 시도하면, DNS 설정 때문에 우리 PC의 가짜 서버로 접속하게 됩니다. 이때, 터미널 화면에 에어컨이 보낸 데이터와 함께 **`추출된 토큰: XXXXXXXX`** 이 출력됩니다.
4.  **설정 원복:** 토큰을 성공적으로 얻었다면, 터미널에서 `Ctrl + C`를 눌러 가짜 서버를 종료하고, **반드시 2단계에서 변경했던 공유기의 DNS 설정을 삭제하여 원상 복구**해야 합니다. 그렇지 않으면 인터넷 사용에 문제가 생길 수 있습니다.

---

## 설정 ⚙️

Homebridge UI의 플러그인 설정 화면에서 '에어컨 추가' 버튼을 눌러 각 장치를 설정합니다.

| 키 | 설명 | 기본값 | 필수 |
| :--- | :--- | :--- | :--- |
| `name` | 홈 앱에 표시될 에어컨의 이름 | - | **예** |
| `ip` | 에어컨의 고정 IP 주소 | - | **예** |
| `token`| 위에서 추출한 인증 토큰 | - | **예** |
| `deviceIndex` | 상태를 **읽어올** 장치의 인덱스(0부터) | `0` | 아니오 |
| `setDeviceIndex`| 명령을 **보낼** 장치의 인덱스 | `deviceIndex` | 아니오 |
| `swingModeType` | 스윙(회전) 기능을 제어할 명령어 타입 | `comfort` | 아니오 |
| `pollingInterval`| 상태 동기화 간격(초). 0이면 비활성화 | - | 아니오 |
| `timeout`| 요청 응답 대기 시간(ms) | `5000` | 아니오 |
| `cacheDuration`| 상태 정보 캐시 유지 시간(ms) | `30000` | 아니오 |
| `debug` | 상세 로그 활성화. 문제 해결 시 사용 | `false` | 아니오 |
| `minTemp` | 설정 가능한 최저 온도 (°C) | `18` | 아니오 |
| `maxTemp` | 설정 가능한 최고 온도 (°C) | `30` | 아니오 |
| `manufacturer`| 홈 앱에 표시될 제조사 이름 | `Samsung` | 아니오 |
| `model`| 홈 앱에 표시될 모델명 | `AC-Model` | 아니오 |
| `serialNumber`| 홈 앱에 표시될 시리얼 번호 | `(이름과 동일)`| 아니오 |

#### `config.json` 직접 수정 예시

```json
{
  "bridge": {
    "...": "..."
  },
  "platforms": [
    {
      "platform": "SamsungACPlatform",
      "name": "Samsung ACs",
      "accessories": [
        {
          "name": "거실 에어컨",
          "ip": "192.168.1.50",
          "token": "YOUR-EXTRACTED-TOKEN-HERE",
          "pollingInterval": 30,
          "debug": false,
          "minTemp": 18,
          "maxTemp": 30
        },
        {
          "name": "침실 에어컨",
          "ip": "192.168.1.51",
          "token": "ANOTHER-TOKEN-HERE",
          "pollingInterval": 30
        }
      ]
    }
  ]
}
```

🚀 안정성을 위한 자식 브릿지(Child Bridge) 설정 (강력 추천)
에어컨의 응답 없음 문제가 다른 액세서리에 영향을 주지 않도록, 이 플러그인을 자식 브릿지에서 실행하는 것을 강력히 권장합니다.

플러그인 설정 이동: 홈브릿지 UI의 '플러그인' 탭에서 Homebridge Samsung Ac을 찾습니다.

설정 아이콘 클릭: 플러그인 우측의 공구 모양(설정) 아이콘을 클릭합니다.

브릿지 설정: 팝업 메뉴에서 **'브릿지 설정(Bridge Settings)'**을 선택합니다.

자식 브릿지로 실행: '별도의 자식 브릿지에서 실행(Run in a separate Child Bridge)' 옵션을 켭니다.

저장 및 재시작: 설정을 **저장(Save)**하고, 홈브릿지를 재시작하면 완료됩니다.

⚠️ 보안 경고
이 플러그인은 오래된 보안 프로토콜(TLSv1)을 사용하여 에어컨과 통신합니다. 신뢰할 수 있는 로컬 네트워크 환경에서만 사용하시는 것을 강력히 권장합니다.
