homebridge-smartthings-km81
v2.16.2
Published
Unified Homebridge plugin for Samsung devices via SmartThings (legacy AC over TLSv1, modern/system AC, washer/dryer with notification sensors).
Maintainers
Readme
homebridge-smartthings-km81
삼성 에어컨·세탁기·건조기를 HomeKit에 연결하는 Homebridge 플러그인입니다.
가장 큰 특징은 로컬 제어입니다. 대부분의 기기를 SmartThings 클라우드를 거치지 않고 집 안 네트워크에서 직접 제어합니다. 인터넷이 끊겨도 동작하고, 반응이 빠르며, 클라우드 API 사용량에 영향을 받지 않습니다.
지원 기기
| 기기 | 통신 방식 | 클라우드 | |---|---|---| | 삼성 에어컨 (2016~2018년경, 2in1 포함) | 로컬 TCP 8888 | 불필요 | | 삼성 에어컨 (2023년 이후) | 로컬 CoAP over DTLS | 선택 | | 삼성 건조기 | 로컬 CoAP over DTLS 또는 클라우드 | 선택 | | 삼성 세탁기 (2-in-1 포함) | 로컬 TCP 8888 또는 클라우드 | 선택 | | 삼성 정수기 | 로컬 CoAP over DTLS | 불필요 |
HomeKit에는 이렇게 보입니다.
- 에어컨 — 냉난방기 (전원·온도·모드·무풍·잠금)
- 세탁기·건조기 — 스프링클러 밸브(남은 시간 카운트다운)
- 운전이 끝나면 알림을 받고 싶으면 설정에서
종료 알림 센서 활성화을 켜세요(기본은 꺼짐). 켜면 모션 센서가 하나 더 생기고, 그걸로 홈 앱 자동화를 걸 수 있습니다.
- 운전이 끝나면 알림을 받고 싶으면 설정에서
- 정수기 — ⚠️홈킷에는 안 나옵니다. 홈킷에 담을 값어치가 있는 건 잠금 정도인데,
필터 잔여량·출수량 같은 계량값은 홈킷이 담는 그릇이 아닙니다(부피 특성이 아예 없어
조도 센서같은 거짓 표시로 우회해야 합니다). 그래서 Home Assistant 로만 중계합니다 — 필터 잔여량·상태·출수 중·잠금 3종·온수 온도·출수량·살균 일정·자가진단. ⚠️MQTT 를 꺼 두면 이 기기는 아무 데도 나가지 않습니다(홈킷 액세서리가 없으니까요).
먼저 알아야 할 것 — 기기를 어떻게 지목하나
구형 에어컨을 뺀 나머지 기기(신형 에어컨·세탁기·건조기)는 IP만으로는 부족합니다. 플러그인이 "이 설정 항목이 어느 기기인지" 알아야 하는데, 그 수단이 둘 중 하나입니다.
| 방법 | 무엇을 적나 | 언제 쓰나 | SmartThings 연결 |
|---|---|---|---|
| 장치 이름 (deviceLabel) | SmartThings 앱에 보이는 이름을 글자 하나까지 똑같이 | 처음 설정할 때 | ★필요 |
| SmartThings deviceId | a1b2c3d4-… 형태의 고유 번호 | 이름으로 한 번 붙인 뒤 | 불필요 |
둘 다 비워 두면 그 기기는 만들어지지 않습니다. 로그에
deviceLabel이 비어있는 SmartThings 장치 설정을 건너뜁니다가 뜹니다.⚠️★**
장치 이름으로 찾는 것은 곧 SmartThings 클라우드 조회입니다** — 그래서 이름만 적는 방식은 OAuth 연결이 먼저 끝나 있어야 합니다. (구형 에어컨은 이 절과 무관합니다. IP와 토큰만 있으면 됩니다.)장치 이름만 적어도 동작하지만, 그러면 부팅할 때마다 SmartThings에 목록을 물어봅니다.한 번 붙고 나면 로그에 이런 줄이 나옵니다:
↳ deviceId=a1b2c3d4-… — config에 적어두면 다음 부팅부터 클라우드 조회를 건너뜁니다이 값을
SmartThings deviceId에 적어 두세요. 그 뒤로는 부팅할 때 클라우드를 아예 부르지 않습니다.
구형 에어컨은 SmartThings와 무관하게 IP+토큰으로만 동작하므로 이 항목이 없습니다.
설치
Homebridge UI의 플러그인 검색에서 homebridge-smartthings-km81을 설치하거나:
npm install -g homebridge-smartthings-km81도커로 홈브릿지를 쓰신다면
-g는 홈브릿지가 보지 않는 곳에 설치될 수 있습니다. 그럴 땐 컨테이너 안에서 이렇게 설치하세요.npm install --prefix /var/lib/homebridge homebridge-smartthings-km81
설치 후 Homebridge UI의 플러그인 설정 화면에서 기기를 추가합니다. 모든 항목에 한국어 설명이 붙어 있습니다.
기기별 설정
구형 에어컨 (2016~2018년경)
장치 종류를 구형 에어컨으로 고르고 아래를 채웁니다.
| 항목 | 값 | |---|---| | 이름 | HomeKit에 표시할 이름 | | 에어컨 IP | 공유기에서 고정 IP로 잡아두세요 | | 인증 토큰 | 토큰 추출 참고 |
냉방 버튼을 누르면 무엇이 되나 — 홈킷 에어컨 타일에는 냉방·난방·자동만 있습니다(북미 냉난방
시스템 기준). 한국 냉방기에 맞춰 이 플러그인은 냉방만 쓰고, 그 버튼이 실제로 어떤 모드를
보낼지 냉방 버튼 → 보낼 모드에서 고릅니다.
| 고를 수 있는 값 | 기기 동작 | |---|---| | 냉방 / 냉방청정 | 냉방(청정은 공기청정 동반) | | 제습 / 제습청정 | 제습 | | 송풍 | 바람만 — 냉방하지 않습니다 | | 자동 | 기기가 알아서 (삼성 앱의 자동 계열 모드) |
송풍·자동은 냉방을 하지 않으므로 홈킷에서 온도를 바꿔도 의미가 없을 수 있습니다. 전원이 켜져 있으면 홈킷 타일은 어느 모드든 '냉방 중'으로 표시됩니다. 기기가 지원하지 않는 모드를 고르면 로그가 지원 목록과 함께 알려 줍니다.
2in1 (실외기 하나에 실내기 둘) — ★읽기/쓰기 인덱스가 교차인 기기가 있습니다
같은 IP로 항목을 두 개 만들고, 각 항목에 장치 인덱스 (읽기)와 장치 인덱스 (쓰기)를 넣습니다.
기기가 상태를 보고하는 순서와 명령을 받는 순서가 모델에 따라 반대인 경우가 있어 둘이 나뉘어 있습니다.
먼저 이 조합으로 해 보세요
쓰기를 읽기와 교차로 둔 조합입니다.
| 항목 | 장치 인덱스 (읽기) | 장치 인덱스 (쓰기) |
|---|---|---|
| 거실 에어컨 | 1 | 0 |
| 침실 에어컨 | 0 | 1 |
{ "deviceType": "legacyAc", "name": "거실 에어컨",
"ip": "192.168.1.3", "token": "…",
"deviceIndex": 1, "setDeviceIndex": 0 },
{ "deviceType": "legacyAc", "name": "침실 에어컨",
"ip": "192.168.1.3", "token": "…",
"deviceIndex": 0, "setDeviceIndex": 1 }같은 IP·같은 토큰을 두 항목에 그대로 넣고, 두 인덱스만 다르게 둡니다.
★이대로 안 되면 반대로 해 보세요 — 방마다 숫자를 맞바꾸는 것입니다 (거실 읽기 0/쓰기 1, 침실 읽기 1/쓰기 0). 어느 방이 인덱스 0인지는 실외기에 실내기를 물린 설치 시점 배선에 달려 있어 집마다 다릅니다. 두 조합 중 하나는 맞습니다.
인덱스가 틀렸을 때 나타나는 증상
아래 중 하나라도 보이면 인덱스 조합이 맞지 않는 것입니다 — 위의 "반대로" 조합을 먼저 시도하세요.
- 홈 앱에서 엉뚱한 방이 켜진다
- 상태(온도·전원)는 잘 보이는데 제어가 안 된다
- 켜기를 눌러도 반응이 없고, 로그에
전원 → 켜짐은 있는데 그 뒤전송 →줄이 없다 - 로그에
켜기가 반영되지 않았습니다+쓰기 인덱스가 읽기와 교차일 수 있습니다경고가 뜬다
왜 이런 증상이 되나: 쓰기가 옆 방으로 나가면 자기 상태가 안 바뀝니다. 그러면 다음에 반대 방향을 눌렀을 때 플러그인이 "이미 그 상태네" 하고 명령을 생략해 아무 일도 일어나지 않습니다.
두 조합 다 안 될 때 — 시험 2개로 직접 정하기 (읽기와 쓰기는 따로 확인합니다)
위 두 조합은 "쓰기가 교차"라는 전제를 공유합니다. 읽기만 교차이거나 둘 다 정상인 기기도 있을 수 있으므로, 그때는 아래로 각각 판정하세요.
시험 ① 읽기 — 리모컨으로 한쪽 방만 켠 뒤, 홈 앱에서 어느 타일이 켜지는지 봅니다.
| 결과 | 판정 | |---|---| | 켠 방의 타일이 켜짐 | 읽기 정상 — 그대로 | | 반대 방 타일이 켜짐 | 읽기 교차 — 두 항목의 읽기 인덱스를 서로 바꿈 |
시험 ② 쓰기 — 홈 앱에서 한쪽 타일을 켠 뒤, 실제로 어느 방이 켜지는지 봅니다.
| 결과 | 판정 | |---|---| | 누른 타일의 방이 켜짐 | 쓰기 정상 — 그대로 | | 반대 방이 켜짐 (또는 아무 반응 없음) | 쓰기 교차 — 두 항목의 쓰기 인덱스를 서로 바꿈 |
두 시험은 서로 독립입니다 — 읽기만 교차, 쓰기만 교차, 둘 다 교차가 모두 가능합니다.
신형 에어컨·시스템 에어컨·건조기 (2023년 이후)
시스템 에어컨은 장치 종류에서 시스템 에어컨을 고릅니다. 적는 항목과 동작은
신형 에어컨과 같습니다.
스윙 토글로 무엇을 켤지 — 스윙(Swing) 토글 ↔ 기능에서 고릅니다. 기기 종류마다 목록이 다릅니다.
| 장치 종류 | 고를 수 있는 값 | |---|---| | 구형 에어컨 | 무풍 / 회전 / 사용 안 함 | | 신형 에어컨 | 무풍 / 회전 / 사용 안 함 | | 시스템 에어컨 | 무풍 / 상하좌우 / 상하 바람 / 좌우 바람 / 사용 안 함 |
회전은 기기에 물어 지원하는 방향으로 돕니다 — 어느 방향인지 몰라도 됩니다. 시스템 에어컨은 보통 여러 방향을 지원하므로 직접 고르게 해 두었습니다. 토글을 끄면 항상 고정을 보냅니다. 기기가 지원하지 않는 방향을 고르면 로그가 지원 목록과 함께 알려 줍니다.
⚠️바람방향은 전송 경로가 로컬일 때만 동작합니다 — SmartThings 클라우드에는 이 기능이 없습니다.
시스템 에어컨은 홈 앱에서 온도를 0.5℃ 단위로 맞춥니다(신형·구형 에어컨은 1℃). 기기가 1℃ 단위여도 문제되지 않습니다 — 플러그인이 기기에 맞는 값으로 바꿔 보냅니다.
⚠️이 종류를 쓰다가 2.7.x 이하로 되돌리려면, 먼저
장치 종류를 신형 에어컨으로 바꾸세요. 구버전은시스템 에어컨을 모르기 때문에 그 기기를 "설정에 없는 것"으로 보고, 홈킷에 남아 있던 액세서리를 경고 없이 지웁니다. 그러면 그 기기에 걸어 둔 홈 앱 자동화와 방 배치가 함께 사라집니다. (2.8.0부터는 반대 방향 — 모르는 종류를 만나면 지우지 않고 경고합니다.)
온도를 읽고 쓰는 리소스 경로는 보드마다 다릅니다. 천장형과 일부 벽걸이는 표준 경로가 없고 제조사 경로만 있습니다. 플러그인이 첫 조회 때 기기에 물어 판별하므로 설정할 것은 없습니다. 제조사 경로를 쓰게 되면 로그에 한 줄 남습니다.
[거실 에어컨] 이 기기에는 표준 온도 리소스가 없어 제조사 경로(/temperatures/vs/0)를 씁니다
설정 방법이 두 가지입니다. 로컬로만 쓸 거라면 A가 짧습니다.
| | A. 로컬 전용 | B. 클라우드 폴백까지 | |---|---|---| | 적을 것 | 기기 IP만 | 장치 이름 + 기기 IP | | SmartThings 연결 | 불필요 | 필요 | | 로컬이 실패하면 | 그 기기는 제어되지 않음 | 클라우드로 넘어감 |
A. 로컬 전용 — 기기 IP만
장치 종류를 신형 에어컨·시스템 에어컨·건조기 중에서 고르고, 전송 경로를 로컬,
기기 IP를 채운 뒤 로컬 실패 시 클라우드 사용을 끕니다. 장치 이름과 deviceId는 비워 둡니다.
플러그인이 부팅할 때 그 IP의 기기에 물어 deviceId를 알아냅니다. 로그에 이렇게 나옵니다.
SmartThings 연결 없이 로컬 전용으로 동작합니다 (클라우드 호출 0회).
[신형 에어컨] deviceId를 기기에서 확인했습니다 — Samsung Window A/C한 번 알아낸 값은 저장되므로, 이후 부팅에서는 기기가 꺼져 있어도 됩니다.
clientId·clientSecret·redirectUri는 비워 두면 됩니다.
⚠️세탁기는 이 방식이 안 됩니다. 8888 토큰 방식이라 기기에 물어보는 경로가 없습니다. 세탁기는 B를 쓰거나 deviceId를 직접 적어야 합니다.
B. 클라우드 폴백까지 — 전체 흐름
1단계 SmartThings OAuth 연결 ← 계정당 1회. 아래 'SmartThings 클라우드를 쓸 때' 절
↓
2단계 장치 이름 + 기기 IP 입력 → 재시작 ← 이름으로 기기를 찾아 붙습니다
↓
3단계 로그에서 deviceId를 복사해 설정에 붙여넣기 ← 이제 부팅 때 클라우드 조회 0회2단계까지만 해도 동작합니다. 3단계는 부팅 시 클라우드 조회를 없애는 과정입니다.
1단계 — SmartThings 연결
SmartThings 클라우드를 쓸 때 절을 먼저 끝내세요. ⚠️여기에 https 리버스 프록시가 필요합니다.
2단계 — 기기 설정
장치 종류를 신형 에어컨·시스템 에어컨·건조기 중에서 고르고 아래를 채웁니다.
| 항목 | 값 | |---|---| | 장치 이름 | SmartThings 앱에 보이는 이름과 글자 하나까지 똑같이. 앱에서 이름이 겹치지 않게 해 두세요 | | 전송 경로 | 로컬 | | 기기 IP | 공유기에서 고정 IP로 잡아두세요. 포트는 자동으로 찾습니다 | | SmartThings deviceId | 2단계에서는 비워 둡니다. 3단계에서 채웁니다 |
기기 토큰은 필요 없습니다(구형과 다른 점).
준비물 3가지 — 이게 없으면 로컬이 안 붙습니다:
- ★1단계(SmartThings 연결)가 끝나 있을 것. 이름으로 기기를 찾는 것이 곧 클라우드 조회입니다.
OAuth 없이 이름만 적으면
'…'에 해당하는 장치를 SmartThings에서 찾지 못했습니다가 뜹니다. - ★파이썬 3.11 이상 + pip — 신형 기기는 DTLS로 통신하는데 Node에 DTLS가 없어 파이썬 도우미를 씁니다.
플러그인이 첫 기동 때 자동으로 설치합니다(
smartthings-local). ⚠️3.11 미만이면 설치가 안 됩니다 — 라즈베리파이 OS Bullseye(3.9)·구형 데비안이 여기 해당합니다. 새 파이썬을 깐 뒤 설정localPythonBin에 그 경로(예:/usr/bin/python3.12)를 지정하세요. - 처음 한 번의 인터넷 — 두 곳에 접속합니다: PyPI(파이썬 패키지)와
connect-v2.samsungiotcloud.com:443(기기와 통신할 인증서 발급). 기기를 격리 VLAN에 두셨다면 이 두 곳이 열려야 합니다. 한 번 끝내면 그 뒤로는 인터넷 없이 동작합니다.
3단계 — deviceId를 받아 적기
2단계로 재시작하면 로그에 이 줄이 나옵니다.
'승준 에어컨' (smartAc) HomeKit 추가/갱신
↳ deviceId=3ea2a924-… — config에 적어두면 다음 부팅부터 클라우드 조회를 건너뜁니다이 값을 SmartThings deviceId 칸에 붙여넣고 재시작하면, 그 뒤로는 부팅할 때
클라우드를 부르지 않습니다.
OAuth 항목이 필요 없는 조건
clientId·clientSecret·redirectUri는 클라우드를 실제로 쓰는 기기가 하나라도 있을 때만
필요합니다. 아래를 모두 만족하는 기기만 있으면 비워 둬도 됩니다.
전송 경로가 로컬이고기기 IP가 적혀 있다로컬 실패 시 클라우드 사용이 꺼져 있다- (세탁기처럼 8888 토큰을 쓰는 기기라면)
SmartThings deviceId도 적혀 있다
하나라도 어긋나면 OAuth 항목이 필요하다는 오류가 뜨고, 어떻게 하면 안 필요한지 함께 나옵니다.
파이썬 도우미와 인증서는 어디에 생기나 — 홈브릿지 저장 폴더 밑 .km81-local/입니다
(도커면 /homebridge/.km81-local, 라즈베리파이 등에 직접 설치하셨으면 /var/lib/homebridge/.km81-local).
node_modules 밖이라 플러그인을 업데이트해도 남습니다. 다른 곳에 두려면 설정 localStateDir.
파이썬 버전을 바꾸면(예: localPythonBin 변경) 의존성을 다시 설치합니다.
로컬이 안 붙을 때 — 이 로그를 먼저 보세요
| 로그 | 뜻과 조치 | |---|---| |
로컬 경로 의존성 최초 설치 — 잠시 걸립니다→설치됨| 정상입니다 | |파이썬이 시스템 보호(PEP 668)로 설치를 막았습니다| 플러그인이 자동으로 다시 시도합니다. 그냥 두세요 | |파이썬 3.11 이상이 필요합니다| 새 파이썬 설치 후localPythonBin에 경로 지정 | |설치할 수 있는 버전을 찾지 못했습니다| 파이썬 버전이 낮습니다. 위와 같이 조치하세요 | |권한 문제로 실패|localStateDir에 홈브릿지가 쓸 수 있는 폴더를 지정 | |파이썬을 찾을 수 없습니다| 파이썬을 설치하거나, 설정localPythonBin에 실행 경로를 지정 | |파이썬에 pip이 없습니다|python3 -m ensurepip --upgrade또는apt install python3-pip| |네트워크 문제로 실패| 최초 1회는 PyPI 접속이 필요합니다. 연결 후 재시작 | |의존성 설치 실패 (코드 N) — pip 출력: …| 그 pip 출력이 원인입니다. 안내된 명령을 직접 돌려보세요 | |인증서 발급 실패: Hash algorithm "sha1" not supported…| v2.6.7에서 해결됐습니다. 업데이트하세요 |첫 기동에서
로컬 경로 최초 설치가 진행 중입니다가 보이면 정상입니다 — 몇 분 뒤 자동으로 로컬 제어로 전환됩니다. 설치가 실패해도 다음 재시작에서 자동으로 다시 시도합니다. 그동안로컬 실패 시 클라우드 사용이 켜져 있으면 클라우드로 계속 동작하고, 꺼져 있으면 그 기기는 제어되지 않습니다(홈 앱에응답 없음).
세탁기
장치 종류를 세탁기로 고릅니다.
| 항목 | 값 | |---|---| | 장치 이름 / SmartThings deviceId | 위 절 참고 — 하나는 반드시 필요 | | 전송 경로 | 로컬 | | 기기 IP | 공유기에서 고정 IP로 잡아두세요 | | 기기 토큰 | 아래에서 얻습니다 |
- 토큰을 비워두면 클라우드로 동작합니다.
- ★세탁기는 전원을 끄면 네트워크에서 사라집니다. 그래서 꺼져 있는 동안 로그에
전원 꺼짐 — 로컬 응답 없음이 한 줄 뜨는 것이 정상이고, 홈 앱에는 '완료' 상태로 보입니다. 설정 직후 세탁기가 꺼져 있다면 이 줄이 곧 "제대로 붙었다"는 신호입니다. - 2-in-1(애드워시+콤팩트워시)은 기본적으로 하나로 합쳐 보이고, 둘 중 하나만 돌아도 "가동 중"으로 표시합니다.
세탁조를 따로 표시를 켜면 각각 별도 액세서리가 됩니다.
SmartThings 클라우드를 쓸 때
로컬로 붙지 않는 기기, 또는 로컬 실패 시 폴백을 쓰려면 SmartThings OAuth가 필요합니다.
처음 설치할 때는 대체로 한 번은 필요합니다. 신형 에어컨·세탁기·건조기는
SmartThings deviceId로 지목하는 게 가장 깔끔한데, 그 ID를 알려면 한 번은 SmartThings에 물어봐야 하기 때문입니다(로그가 알려줍니다 — 위 절).한 번 받아 적은 뒤에 모든 기기를 로컬로 쓰고 폴백도 전부 끄면, 그때부터는 클라우드를 아예 부르지 않습니다(토큰 유지용 하루 1회 갱신도 자동으로 꺼집니다). 다만 OAuth 설정을 지우면 시작할 때 "인증이 필요합니다" 안내가 계속 뜹니다 — 동작에는 지장 없습니다.
먼저 알아야 할 것 — Redirect URI는 반드시 https
SmartThings는 Redirect URI로 https://만 받습니다. http://192.168.0.10:8999/callback 같은 주소는 등록 자체가 거부됩니다.
그런데 이 플러그인이 띄우는 인증 서버는 평문 HTTP, 포트 8999 고정입니다. 그래서 둘을 이어 줄 것이 필요합니다.
SmartThings ──https──▶ 리버스 프록시 ──http──▶ 홈브릿지 :8999
(인터넷) (TLS 종료) (인증 서버)리버스 프록시(Nginx Proxy Manager, Caddy, Cloudflare Tunnel 등)로 도메인 하나를 8999로 넘기면 됩니다. 이미 홈브릿지 UI를 외부에서 https로 쓰고 있다면 같은 방식으로 하나 더 만들면 됩니다.
인증은 한 번만 하면 되므로, 프록시를 상시 두기 싫다면 인증할 때만 잠깐 열었다 닫아도 됩니다.
절차
SmartThings 개발자 워크스페이스에서 New Project → Device Integration → SmartThings Cloud Connector → OAuth-In을 만듭니다. (터미널이 편하면
npm i -g @smartthings/cli뒤smartthings apps:create로도 같은 것을 만들 수 있습니다.)권한(Scope)은 세 개 모두 선택합니다.
r:devices:*(상태 읽기)w:devices:*(설정 쓰기)x:devices:*(명령 실행)
하나라도 빠지면 인증은 되는데 제어가 안 됩니다.
Redirect URI를 등록합니다. 예:
https://homebridge.example.com/oauth/callback- 경로(
/oauth/callback)는 원하는 대로 정해도 됩니다. 플러그인은 경로만 보고 콜백을 받습니다. - 이 주소가 리버스 프록시를 거쳐 홈브릿지의 8999 포트에 닿아야 합니다.
- 경로(
발급된 Client ID와 Client Secret, 그리고 방금 등록한 Redirect URI를 플러그인 설정에 그대로 넣습니다. 세 값은 워크스페이스에 등록한 것과 글자 하나까지 같아야 합니다.
Homebridge를 재시작하면 로그에 이런 안내가 나옵니다.
====================[ 스마트싱스 인증 필요 ]==================== 1. 임시 인증 서버가 포트 8999에서 실행 중입니다. 2. 아래 URL을 복사하여 웹 브라우저에서 열고 … 인증 URL: https://api.smartthings.com/oauth/authorize?client_id=…그 인증 URL을 브라우저에서 열고 SmartThings 계정으로 로그인해 권한을 허용합니다. 승인하면 브라우저가 Redirect URI로 이동하고, 플러그인이 토큰을 받아 저장합니다. 이후에는 자동으로 갱신되므로 다시 할 일이 없습니다.
인증이 안 될 때
| 증상 | 원인과 조치 |
|---|---|
| 워크스페이스가 Redirect URI를 거부 | https가 아니거나 IP 주소입니다. 도메인 + https로 등록하세요. |
| 승인 후 브라우저가 오류 페이지 | 그 도메인이 홈브릿지 8999에 닿지 않는 것입니다. 프록시 설정을 확인하세요. |
| 브라우저는 성공했는데 로그가 조용함 | 콜백이 홈브릿지까지 못 온 것입니다. 10분 뒤 로그에 콜백이 오지 않았습니다 안내가 뜹니다 — 프록시가 그 주소를 8999로 넘기는지 확인하세요. |
| 로그에 포트 8999를 사용할 수 없습니다 | 다른 프로세스가 8999를 쓰고 있습니다. |
| 승인은 됐는데 기기 제어가 안 됨 | 권한 세 개를 다 골랐는지 확인하고, 워크스페이스에서 고친 뒤 다시 인증하세요. |
| redirectUri가 유효한 URL 형식이 아닙니다 | 설정에 넣은 값에 오타가 있거나 https://가 빠졌습니다. |
공통 옵션 — 임시 연결해제
계절용 기기처럼 당분간 전원을 뽑아 둘 기기는 기기 설정에서 임시 연결해제 를 체크합니다.
체크하면 그 기기와 통신을 아예 하지 않습니다. 기동할 때 안내 한 줄만 남고 그 뒤로는 조용합니다.
[거실 에어컨] 임시 연결해제 — 통신 안 함- 홈 앱 타일은 그대로 둡니다 — 방 배치와 장면이 사라지지 않고, 타일은 마지막으로 알고 있던 상태를 그대로 보여 줍니다(정상 연결로 보입니다).
- 타일을 눌러도 기기에는 전달되지 않습니다. 값이 잠깐 바뀌었다가 곧 원래 값으로 돌아갑니다 (누른 값이 「마지막 상태」로 저장되지 않게). 무풍·자동건조 스위치, 보조 세탁조, 종료 알림 센서처럼 따로 보이는 타일도 똑같이 동작합니다.
- 설정은 기동할 때 한 번만 읽습니다 — 체크를 바꾸면 홈브릿지를 다시 시작해야 적용됩니다.
- Home Assistant 로 중계하던 기기라면 중계가 멈춥니다. 엔티티는 남지만 값이 더 갱신되지 않습니다. (엔티티를 지우지는 않습니다 — 지우면 그 엔티티를 쓰던 자동화가 깨지기 때문입니다.)
- 다시 쓰려면 체크를 풀고 홈브릿지를 다시 시작하면 됩니다.
⚠️ 체크해 둔 기기는 어떤 이상도 보고하지 않습니다. 다시 쓰기 시작할 때 체크를 푸는 것을 잊지 마세요.
기기 토큰 추출하기
TCP 8888로 통신하는 기기(구형 에어컨, 세탁기)는 기기가 발급하는 토큰이 있어야 합니다. 한 번 받으면 계속 쓸 수 있습니다.
원리
기기에 "토큰을 달라"고 요청하면, 기기가 당신의 컴퓨터로 되전화를 걸어 토큰을 건네줍니다. 그래서 두 가지가 필요합니다.
- 되전화를 받을 수신 대기 프로그램 (포트 8889)
- 요청할 때 어디로 걸어야 하는지 알려 주는 것 —
Host헤더
⚠️ 이
Host헤더를 빠뜨리면 기기가 자기 자신에게 요청을 보내고 토큰이 오지 않습니다.
준비물
- 기기와 같은 네트워크에 있는 컴퓨터 (요청과 수신을 같은 컴퓨터에서 해야 합니다)
- Python 3
- 플러그인에 들어 있는 인증서 —
node_modules/homebridge-smartthings-km81/cert/cert.pem
아래 두 스크립트를 그 인증서와 같은 폴더에 두고 실행하세요.
1단계 — 수신 대기
listener.py로 저장하고 실행합니다.
import re, socket, ssl, threading
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ctx.minimum_version = ssl.TLSVersion.TLSv1
ctx.set_ciphers('ALL:@SECLEVEL=0')
ctx.verify_mode = ssl.CERT_NONE
ctx.load_cert_chain('cert.pem')
def handle(sock):
data = b''
sock.settimeout(10)
try:
while b'}' not in data and len(data) < 65536:
chunk = sock.recv(4096)
if not chunk:
break
data += chunk
except Exception:
pass
m = re.search(r'"DeviceToken"\s*:\s*"([^"]+)"', data.decode('utf-8', 'replace'))
if m:
print('\n★ 토큰:', m.group(1), '\n')
sock.sendall(b'HTTP/1.1 200 OK\r\nContent-Length: 0\r\nConnection: close\r\n\r\n')
sock.close()
srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
srv.bind(('0.0.0.0', 8889)) # bind가 먼저 — 실패하면 여기서 즉시 멈춥니다
srv.listen(5)
print('대기 중 — 0.0.0.0:8889')
while True:
raw, addr = srv.accept()
print('연결 수신:', addr[0])
try:
conn = ctx.wrap_socket(raw, server_side=True)
except Exception as e:
print('TLS 실패:', e)
continue
threading.Thread(target=handle, args=(conn,), daemon=True).start()python3 listener.py대기 중 문구가 떠야 합니다. 안 뜨면 8889를 다른 프로그램이 쓰고 있는 것이니 정리하고 다시 실행하세요.
기기에 따라 TLS가 아니라 평문으로 되전화를 거는 경우가 있습니다.
연결 수신은 찍히는데 곧바로TLS 실패가 뜬다면 그 경우입니다. 그럴 땐 위 코드에서ctx.wrap_socket(...)줄을 빼고conn = raw로 바꿔 한 번 더 시도해 보세요.
2단계 — 기기 준비
에어컨 — 전원을 끕니다 (콘센트는 그대로).
세탁기 — 전원을 켜고, 문을 닫고, 패널의 스마트 컨트롤(원격 제어) 버튼을 짧게 눌러 램프를 켭니다.
⚠️ 3초 이상 길게 누르면 Wi-Fi 페어링(AP) 모드로 들어가 네트워크에서 빠집니다. 그러면 껐다 켜고 다시 하세요.
3단계 — 토큰 요청
request.py로 저장하고, IP 두 개를 자기 환경에 맞게 고쳐 새 터미널에서 실행합니다.
import ssl
from http.client import HTTPSConnection
DEVICE_IP = '192.168.1.50' # 기기 IP
LISTENER_IP = '192.168.1.100' # 이 스크립트를 실행하는 컴퓨터 IP
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.check_hostname = False # 순서 주의: verify_mode보다 먼저
ctx.verify_mode = ssl.CERT_NONE
ctx.minimum_version = ssl.TLSVersion.TLSv1
ctx.maximum_version = ssl.TLSVersion.TLSv1_2 # TLS 1.3을 보내면 기기가 멈춥니다
ctx.set_ciphers('DEFAULT@SECLEVEL=0')
ctx.load_cert_chain('cert.pem')
body = '{}'
conn = HTTPSConnection(DEVICE_IP, 8888, context=ctx, timeout=15)
conn.putrequest('POST', '/devicetoken/request', skip_host=True, skip_accept_encoding=True)
conn.putheader('Host', f'{LISTENER_IP}:8889') # ★되전화 주소. 빠뜨리면 실패합니다
conn.putheader('Content-Type', 'application/json')
conn.putheader('DeviceToken', 'xxxxxxxxxxx') # 그대로 두세요 (자리표시자)
conn.putheader('Content-Length', str(len(body)))
conn.endheaders(body.encode())
r = conn.getresponse()
print(r.status, r.reason)python3 request.py200 OK가 나오면 요청이 접수된 것입니다.
4단계 — 기기를 켭니다
에어컨 — 전원을 켭니다. 세탁기 — 전원을 껐다가 다시 켭니다.
몇 초 안에 수신 대기 창에 토큰이 찍힙니다.
연결 수신: 192.168.1.50
★ 토큰: aB3dEf7hIj이 값을 플러그인 설정의 인증 토큰(에어컨) 또는 기기 토큰(세탁기)에 넣으면 됩니다.
잘 안 될 때
| 증상 | 원인과 조치 |
|---|---|
| 403 … previous request | 직전 요청이 처리 중입니다. 정상이니 1분 기다렸다 다시 하세요. |
| 200 OK인데 토큰이 안 옴 | Host 헤더의 IP가 수신 대기 중인 컴퓨터의 것이 맞는지 확인하고, 4단계(전원 껐다 켜기)를 다시 하세요. |
| 수신 창에 아무 연결도 안 잡힘 | 방화벽이 8889 인바운드를 막는지, 컴퓨터와 기기가 같은 네트워크인지 확인하세요. |
| 연결 자체가 안 됨 | 기기가 켜져 있는지, IP가 맞는지 확인하세요. 세탁기는 전원을 끄면 네트워크에서 사라집니다. |
| curl로는 실패함 | 최신 curl은 TLS 1.0을 거부합니다. 위 Python 스크립트를 쓰세요. |
MQTT 브리지 (Home Assistant 중계)
로컬로 제어 중인 기기의 상태를 MQTT 브로커로 내보내, Home Assistant가 자동 검색으로 엔티티를 만들게 합니다. HA는 기기에 직접 붙지 않습니다 — 기기와의 세션(신형 DTLS·구형 8888)은 이 플러그인이 단독으로 소유하고, HA는 브로커만 봅니다. 세션이 기기당 하나뿐이라 HA가 직접 붙으면 홈브릿지와 서로 끊기기 때문입니다.
먼저 필요한 것
- MQTT 브로커 — 예: Mosquitto (Home Assistant 애드온으로 한 번에 깔거나, 도커로 띄웁니다). 브로커에 이 플러그인용 계정(사용자 이름/비밀번호)을 하나 만들어 두세요.
- HA에 MQTT 통합이 연결돼 있어야 자동 검색이 동작합니다 (HA 설정 → 기기 및 서비스 → MQTT).
설정
| 항목 | 값 | |---|---| | MQTT 브리지 사용 | 켬 (기본은 꺼짐 — 꺼져 있으면 아무 것도 하지 않습니다) | | 브로커 주소 | 브로커가 도는 서버의 IP (보통 HA/NAS와 같은 주소) | | 브로커 포트 | 기본 1883 그대로 | | 사용자 이름 / 비밀번호 | 브로커에 만든 계정입니다 (HA 로그인 계정이 아닙니다) | | 기본 토픽 / HA 자동 검색 접두어 / 재발행 주기(초) | 기본값 그대로 두면 됩니다 |
저장하고 재시작하면 로그에 MQTT 브리지 연결됨과 기기별 중계 시작이 뜨고,
잠시 뒤 HA의 MQTT 통합 아래에 기기들이 자동으로 나타납니다. HA 쪽에서는 아무 설정도 필요 없습니다.
브로커가 없거나 값이 잘못돼도 홈킷 동작에는 영향이 없습니다 — 이 브리지는 곁가지이고, 실패는 자기 선에서 끝납니다.
- 에어컨: 켜기/끄기·온도·무풍·자동건조·
디스플레이 조명을 HA에서 조작할 수 있습니다. HA 조작은 홈킷과 완전히 같은 경로를 타므로, 끄기 억제 창·켜기 후속 순서 같은 안전 장치가 그대로 적용됩니다(조명만은 재점등 위험이 없어 곧바로 전송). 모니터링 센서로순시 전력·누적 전력량·습도·필터 사용률을 내보냅니다 — 이 값들은 클라우드에 없고 로컬에서만 얻어지므로, 클라우드를 끊어도 유지됩니다. - 세탁기·건조기: 읽기 전용입니다(기기가 원격 시작 명령을 받지 않습니다).
가동 중여부·동작 상태·남은 시간을 내보냅니다. 건조기는 로컬에서진행률과누적 전력량도 얻어 함께 내보냅니다(세탁기는 구형 8888이라 이 둘은 제공되지 않습니다). ★세탁기는 전원을 끄면 네트워크에서 사라지는데, 그건 고장이 아니라 '대기 중' 상태로 표현됩니다 — HA에서사용 불가로 뜨지 않습니다.사용 불가는 오직 이 플러그인(브리지)이 멈췄을 때만 뜹니다.
모니터링 센서는 로컬 기기(신형 DTLS)에서만 나오며, HomeKit 특성에 없는 값이라 플러그인이 직접 주기 조회해 발행합니다. 상태는 변화가 있을 때와 주기적으로(기본 60초) 함께 발행되므로, HA나 브로커가 재시작해도 값이 곧 다시 채워집니다.
참고: HA에서 만들어지는 엔티티 이름(entity_id)은 기기 표시 이름을 로마자로 옮긴 형태입니다(예: 승준 에어컨 →
climate.seungjun_eeokeon). 설치 시점의 기기 이름에 따라 정해지며, 자동화에서 참조할 때는 실제 생성된 entity_id를 확인해 쓰세요.
잘 안 될 때
| 증상 | 원인과 조치 |
|---|---|
| 로그: 브로커 주소(host)가 비어 있어 시작하지 않습니다 | 브리지를 켰는데 주소를 안 넣은 것입니다. 브로커 IP를 넣으세요 |
| 로그: MQTT 브로커에 아직 접속하지 못했습니다 | 주소·포트·계정을 확인하세요. 브로커 쪽 방화벽/포트(1883)도 함께 |
| 연결은 됐는데 HA에 기기가 안 생김 | HA에 MQTT 통합이 있는지, HA 자동 검색 접두어가 HA 설정(기본 homeassistant)과 같은지 확인하세요 |
| 세탁기가 HA에서 대기 중으로만 보임 | 정상입니다 — 전원이 꺼져 있는 것입니다. 돌리면 운전 중으로 바뀝니다 |
자주 묻는 것
클라우드 없이 쓸 수 있나요?
네. 모든 기기에 SmartThings deviceId를 적고 로컬로 설정한 뒤 로컬 실패 시 클라우드 사용을 전부 끄면
SmartThings API를 한 번도 부르지 않습니다(토큰 유지용 하루 1회 갱신도 자동으로 꺼집니다).
다만 로컬이 실패했을 때 기댈 곳도 없어집니다.
로컬과 클라우드를 섞어 쓸 수 있나요?
네. 기기마다 따로 정합니다. 로컬로 두고 로컬 실패 시 클라우드 사용을 켜 두는 조합을 권합니다.
이 경우 플러그인이 하루 한 번 클라우드 토큰을 갱신해, 폴백이 정작 필요한 순간에 만료돼 있지 않도록 유지합니다.
읽기는 한 번 실패했다고 바로 클라우드로 넘어가지 않고 다음 차례를 한 번 기다립니다 —
잠깐의 끊김 때문에 불필요한 클라우드 호출이 생기지 않게 하기 위해서입니다.
반대로 버튼을 눌러 보내는 명령은 기다리지 않고 즉시 폴백합니다.
세탁기를 껐는데 HomeKit에 계속 "동작 중"으로 보입니다. 세탁기는 전원을 끄면 네트워크에서 사라집니다. 플러그인은 잠시 기다렸다가 "꺼짐"으로 판단해 정리합니다. 운전 중이었다면 일부러 몇 분 기다립니다 — 와이파이가 잠깐 끊긴 것을 "세탁 끝"으로 오인해 거짓 알림을 보내지 않기 위해서입니다.
토큰이 만료되나요? 기기를 초기화하지 않는 한 계속 유효합니다.
기기 IP가 바뀌면? 공유기에서 고정 IP(주소 예약)로 잡아두세요. 바뀌면 설정도 고쳐야 합니다.
세탁기에서 코스나 온도를 바꿀 수 있나요? 아니요. 기기가 원격 변경을 받지 않습니다. 상태 조회와 종료 알림만 됩니다.
로그 읽는 법
기기별 로그에는 앞에 [기기 이름]이 붙습니다. 자주 보게 되는 줄만 모았습니다.
정상입니다 (아무것도 안 해도 됩니다)
| 로그 | 뜻 |
|---|---|
| [세탁기] 전원 꺼짐 — 로컬 응답 없음 | 세탁기 전원이 꺼져 있습니다. 하루에 한 번만 찍히고 조용해집니다 |
| [세탁기] 전원 켜짐 — 로컬 연결됨 | 다시 켜져서 붙었습니다 |
| [건조기] 포트 자동 탐지 → 49155 / DTLS 포트 확인됨 | 신형 기기와 통신 준비 완료 |
| [건조기] 로컬 복귀 — N회 실패 후 정상화 | 잠깐 클라우드로 돌다가 로컬로 돌아왔습니다 |
| 모든 SmartThings 기기가 config의 deviceId로 연결됨 | 부팅할 때 클라우드를 안 불렀다는 뜻 — 가장 좋은 상태입니다 |
확인해 보세요
| 로그 | 무엇을 하나 |
|---|---|
| deviceLabel이 비어있는 SmartThings 장치 설정을 건너뜁니다 | 그 기기에 장치 이름 또는 SmartThings deviceId를 넣으세요 |
| [기기] 인증 실패 (status 401) | 토큰이 틀렸습니다. 구형 에어컨·세탁기는 토큰을 다시 받으세요 |
| [기기] 로컬 경로가 계속 실패해 사실상 클라우드로 동작 중입니다 | IP가 바뀌었거나 기기가 응답하지 않습니다 |
| [기기] 알 수 없는 냉방 모드 '...' | 설정의 냉방 모드 값이 잘못됐습니다(안내에 쓸 수 있는 값이 함께 나옵니다) |
| 설정의 'coolCommand'는 옛 이름입니다 | coolModeCommand로 옮기세요. 그대로 두면 옛 값이 계속 이깁니다 |
| 클라우드 재인증이 필요합니다 | SmartThings 인증을 다시 하세요. 로컬 기기는 계속 동작합니다 |
더 자세한 내용이 필요하면 그 기기 설정에서 디버그 로그를 켜세요. 평소에는 조용하도록 반복되는 실패를 눌러 두기 때문에, 원인을 파고들 때는 디버그가 필요합니다.
문제가 생기면
- 로그를 먼저 — 위 표에서 해당 줄을 찾아보세요.
- 기기 IP 확인 — 공유기에서 고정 IP(주소 예약)로 잡혀 있나요?
- 같은 네트워크인가 — 홈브릿지와 기기가 같은 대역에 있어야 합니다(VLAN 분리 주의).
- 그래도 안 되면 디버그 로그를 켜고 다시 재현해 보세요.
보안 참고
- 기기 토큰은
config.json에 평문으로 저장됩니다. 파일 권한을 확인하세요. - 로컬 통신은 구형 기기가 요구하는 TLS 1.0을 씁니다. 같은 네트워크 안에서만 오가는 통신입니다.
라이선스
MIT
