Skip to content

AI 에이전트용 API ​

이 문서는 AI 에이전트나 외부 자동화 도구가 Homenet2MQTT를 관측·분석·설정할 수 있도록 제공되는 에이전트 API v1 사용법을 정리합니다.

개요 ​

에이전트 API는 UI와 별도 포트(기본 3001, UI의 설정 > Agent API에서 변경)에서 /api/v1/agent 아래에 제공되는 버전 고정 REST API입니다. UI 포트(기본 3000)에서는 /api/v1/agent/*가 404를 반환하며, Agent 포트에서는 UI 정적 파일과 UI API를 제공하지 않습니다. 사용 토글(AGENT_API_ENABLED)이 꺼져 있으면 포트 자체가 열리지 않습니다.

  • OpenAPI 3.1 문서와 평문 가이드를 공개 엔드포인트로 제공합니다.
  • 나머지 모든 엔드포인트는 Bearer 토큰 인증이 필수입니다.
  • 응답은 JSON이며, 오류는 { "error": "...", "message"?: "...", "details"?: ... } 형식입니다.
  • 패킷 관측/분석, 캡처 제어, 엔티티 초안 검증·적용, 실시간 이벤트 스트림을 지원합니다.
  • 초기 설정(예제 선택, serial 테스트, config 생성)과 설정 로드 중 새 브릿지 추가를 API로 수행할 수 있습니다.
  • 같은 기능을 MCP Streamable HTTP 도구로도 제공합니다(POST /api/v1/agent/mcp).
  • 엔티티는 초안 작성 → 검증 → 적용 순서로만 등록할 수 있으며, 적용 후 재시작이 필요합니다.

보안 경고

에이전트 API 토큰은 RS485 버스를 제어하고 설정 파일을 수정할 수 있는 강력한 권한입니다. 토큰을 채팅, 이슈, 로그, URL에 노출하지 마세요. 패킷 전송 기능은 기본적으로 비활성화되어 있습니다.

REST와 MCP 선택 ​

두 인터페이스는 같은 agent/services/* 도메인 로직을 공유합니다. MCP는 REST의 주요 기능을 도구로 노출한 어댑터이므로, 모든 REST 엔드포인트가 MCP 도구로 제공되지는 않습니다.

기준RESTMCP
주요 사용자스크립트, CI, curl, 일반 HTTP 클라이언트MCP를 지원하는 AI 클라이언트(Cursor 등)
호출 방식/api/v1/agent/* + JSONPOST /api/v1/agent/mcp + JSON-RPC 2.0
실시간 이벤트SSE /eventsget_recent_events 폴링
기능 탐색OpenAPI 문서, llms.txttools/list
인증Bearer 토큰동일한 Bearer 토큰
  • 자동화 스크립트, 세밀한 HTTP 제어, SSE 스트리밍이 필요하면 REST를 사용하세요.
  • AI 클라이언트에 도구로 연결하려면 MCP를 사용하세요. MCP는 REST 위에 얹힌 얇은 어댑터입니다.

토큰 설정 ​

에이전트 API는 AGENT_API_ENABLED=true(애드온 옵션 agent_api_enabled)로 활성화하고, Bearer 토큰은 웹 UI에서 자동 생성합니다. 환경변수로 토큰을 지정하지는 않습니다.

토큰이 아직 없거나 파일이 손상되면 모든 인증 엔드포인트가 OpenAPI/llms.txt를 계속 공개한 채 503 AGENT_API_DISABLED를 반환합니다.

  1. 애드온/Docker에서 에이전트 API를 켭니다.
  2. 브라우저로 UI에 처음 접속합니다. Docker에서는 첫 화면에서 UI 비밀번호와 에이전트 API 설정(토큰 자동 생성, 포트, 패킷 전송 허용)을 함께 입력하면 응답으로 토큰 평문이 한 번만 표시됩니다.
  3. ingress로 접속하는 애드온은 비밀번호 입력 화면이 없어서 AI 에이전트 API 설정 화면이 따로 표시됩니다. 여기서 토큰 발급·패킷 전송 허용을 지정한 뒤 계속하면 됩니다. agent_api_enabled가 꺼져 있으면 설정 대신 구성 탭에서 토글을 켜고 네트워크 탭에서 포트를 지정한 뒤 재시작하라는 안내가 표시됩니다. 안내를 건너뛴 뒤 옵션을 켜고 재시작하면 설정 화면이 나타납니다.
  4. 이후에는 설정 > Agent API 화면에서 토큰을 새로 발급(재발급 시 이전 토큰 즉시 무효)하거나 포트·패킷 전송 허용을 바꿉니다.

토큰은 다시 볼 수 없습니다

CONFIG_ROOT/agent-api.json에는 SHA-256 해시만 저장되고 파일 권한은 0600입니다. 평문은 발급 화면에서 한 번만 표시되므로 즉시 안전한 곳으로 복사하세요.

Home Assistant Add-on ​

애드온 설정에는 사용 토글만 있습니다.

yaml
agent_api_enabled: true

MQTT 애드온은 에이전트 포트가 애드온의 네트워크 포트 매핑(3001/tcp)으로 결정되므로 UI에서 변경할 수 없습니다. Home Assistant의 애드온 설정 → 네트워크에서 매핑을 바꾸세요. host_network를 쓰는 Matter 애드온은 설정 > Agent API에서 포트를 변경할 수 있습니다.

애드온 설정 화면에서는 아래 순서로 포트 매핑을 지정합니다.

  1. AI 에이전트 API 섹션의 토글을 켭니다(agent_api_enabled: true).
  2. 네트워크 섹션에서 비활성화된 3001 포트 항목을 누르면 포트 입력창이 나타납니다.
  3. 3001(또는 원하는 다른 포트)을 입력하고 저장한 뒤 애드온을 재시작합니다.

토글을 켜지 않으면 포트 매핑 항목이 활성화되지 않고 포트가 열리지 않은 채로 남아 Agent API에 접근할 수 없습니다.

애드온 설정에서 AI 에이전트 API 토글을 켜고 네트워크의 3001/tcp 포트 매핑을 입력하는 화면

Docker ​

bash
docker run \
  -e AGENT_API_ENABLED=true \
  -p 127.0.0.1:3000:3000 \
  -p 127.0.0.1:3001:3001 \
  ...

토큰 값은 로그에 출력되지 않습니다. run.sh는 AGENT_API: enabled/disabled와 사용 중인 포트만 표시합니다.

노출 및 네트워크 설정 ​

에이전트 API는 전용 포트에서 Bearer 토큰으로 보호됩니다. UI는 별도 포트에서 비밀번호 인증으로 보호되므로, 포트를 분리해 외부에는 Agent 포트만 공개할 수 있습니다. 그래도 필요한 경우 포트 공개 범위를 최소화하고 방화벽/리버스 프록시로 접근 주체를 제한하세요.

Docker ​

deploy/docker/docker-compose.yml의 기본값은 UI(3000)와 Agent API(3001)를 모두 호스트 루프백에만 바인딩합니다.

yaml
environment:
  AGENT_API_ENABLED: 'true'
ports:
  - '127.0.0.1:3000:3000'
  - '127.0.0.1:3001:3001'

포트가 이미 사용 중이라면

지정된 에이전트 포트가 이미 쓰이면 서비스는 최대 9번까지 다음 포트로 자동 이동하고, UI와 Matter 브리지는 그대로 동작합니다. 실제로 바인딩된 포트는 설정 > Agent API 화면과 기동 로그에 표시됩니다. Docker에서 컨테이너 내부 포트가 바뀌었다면 ports:의 컨테이너 쪽 숫자도 함께 수정하세요.

접근 주체주소필요한 설정
같은 호스트의 프로세스http://127.0.0.1:3001기본값
같은 Docker 네트워크의 컨테이너http://<컨테이너명>:3001포트 매핑 불필요
다른 호스트/LANhttp://<H2M-HOST>:3001포트 바인딩 확장 또는 리버스 프록시

같은 Docker 네트워크의 컨테이너에서 호출한다면 포트를 공개할 필요가 없습니다. 두 컨테이너를 사용자 정의 네트워크에 함께 연결하고 서비스 이름으로 접근하세요.

yaml
services:
  homenet2mqtt:
    # ...
    networks: [h2m]

  my-agent:
    # ...
    networks: [h2m]

networks:
  h2m:

LAN에서 직접 접근해야 한다면 바인딩을 특정 호스트 인터페이스로 넓힙니다.

yaml
ports:
  # 예: 호스트 LAN IP가 192.168.0.10인 경우
  - '192.168.0.10:3001:3001'

모든 인터페이스에 열려면 '3001:3001'을 사용하세요. Agent API는 Bearer 토큰으로 보호되지만 토큰이 유출되면 RS485 제어가 가능하므로 방화벽으로 허용 대역을 제한하는 것을 권장합니다.

리버스 프록시로 에이전트 API만 노출 ​

외부 에이전트가 다른 네트워크에서 접근해야 한다면 TLS를 종료하는 리버스 프록시를 두고 Agent 포트(3001)로 전달하는 구성을 권장합니다. Agent 포트에는 UI 라우트가 없으므로 /api/v1/agent/* 경로 제한은 선택 사항입니다.

Caddy

text
h2m.example.com {
  handle /api/v1/agent/* {
    reverse_proxy 127.0.0.1:3001
  }

  handle {
    respond 404
  }
}

Nginx

nginx
location /api/v1/agent/ {
    proxy_pass http://127.0.0.1:3001;
    proxy_http_version 1.1;
    proxy_set_header Connection '';
    proxy_buffering off;
    proxy_read_timeout 3600s;
}

SSE(/api/v1/agent/events)를 사용한다면 응답 버퍼링을 끄고 읽기 타임아웃을 길게 설정하세요.

WARNING

Agent 포트를 외부에 열면 Bearer 토큰을 가진 클라이언트가 RS485 버스와 설정을 제어할 수 있습니다. 토큰을 강하게 관리하고 방화벽/네트워크 정책으로 접근 주체를 제한하세요. UI 포트(3000)는 비밀번호 인증으로 보호되지만 외부 공개 시 함께 방화벽 정책을 적용하는 것이 좋습니다.

Home Assistant Add-on ​

애드온네트워크에이전트 API 접근
Homenet2MQTT (MQTT)ingress 전용, 3001/tcp 선택적 공개포트를 공개한 경우 http://<HA-IP>:<공개포트>
Homenet2Matter (Matter)host_network: truehttp://<HA-IP>:3001으로 직접 접근 가능

MQTT 애드온은 기본적으로 ingress를 통해서만 UI에 접근할 수 있으며, ingress는 3000(UI)만 프록시하므로 Agent API에 사용할 수 없습니다. 외부 에이전트가 필요하면 애드온 구성의 네트워크 섹션에서 3001/tcp를 공개하고 방화벽으로 접근을 제한하세요.

Matter 애드온은 host_network: true이므로 호스트의 3001 포트가 그대로 열립니다. 같은 LAN의 에이전트는 http://<HA-IP>:3001을 사용할 수 있지만, 호스트 방화벽에서 3001 포트 접근을 신뢰할 수 있는 대역으로 제한하세요. UI(3000)는 직접 접속 시 비밀번호를 요구합니다.

인증 ​

http
Authorization: Bearer <AGENT_TOKEN>

<AGENT_TOKEN>은 설정 > Agent API(또는 첫 접속 화면)에서 발급한 평문 토큰입니다.

상황상태 코드error
사용 토글 off / 토큰 미발급 / 파일 손상503AGENT_API_DISABLED
헤더 누락401UNAUTHORIZED
토큰 불일치401UNAUTHORIZED
요청 한도 초과429RATE_LIMITED

503 응답의 reason 필드는 env_disabled(토글 off), no_token(토큰 미발급), store_unavailable(설정 파일 손상) 중 하나입니다.

공개 엔드포인트 ​

MethodPath설명
GET/api/v1/agent/openapi.jsonOpenAPI 3.1 문서
GET/api/v1/agent/llms.txt짧은 평문 사용 가이드

인증 엔드포인트 ​

MethodPath설명
GET/capabilities버전, 지원 기능, 제한, 문서 링크
GET/setup설정 상태, 번들 예제 목록, 사용 중인 portId
GET/setup/examples/:filename예제의 serial/packet 기본값과 선택 엔티티
POST/setup/test-serialserial 배선 테스트 (설정 로드 중에도 가능)
POST/setup/apply초기 설정(init) 또는 브릿지 추가(add)
GET/ports포트별 serial 설정, 브리지 상태, 엔티티 수
GET/entities?portId=정규화된 엔티티 목록
GET/entities/:entityId?portId=단일 엔티티 설정
GET/automations?portId=정규화된 automation 목록
GET/automations/:automationId?portId=단일 automation 설정
GET/scripts?portId=정규화된 script 목록
GET/scripts/:scriptId?portId=단일 script 설정
GET/state?portId=최신 엔티티 상태 스냅샷
GET/packets/history?portId=&limit=&since=parsed/command 패킷 통합 이력
GET/packets/unmatched?portId=미매칭 패킷 + 횟수/최초·최근 시각
POST/packets/analyze활성 또는 초안 설정으로 패킷 분석
POST/packets/preview헤더/푸터/체크섬을 적용한 전송 패킷 미리보기
POST/packets/send실제 RS485 전송 (옵트인 필요)
GET/capture원시 패킷 캡처 상태
POST/capture/start캡처 시작 (mode: all 또는 valid)
POST/capture/stop캡처 종료 및 결과 반환
GET/capture/files캡처 파일 목록
GET/capture/files/:filename캡처 파일 다운로드
DELETE/capture/files/:filename캡처 파일 삭제
GET/gallery?vendor=갤러리 벤더 목록, 또는 벤더별 스니펫 인덱스
GET/gallery/snippet?file=갤러리 스니펫 원본 YAML
GET/gallery/match?portId=&vendor=관측 패킷 대비 갤러리 스니펫 점수화
POST/gallery/expand스니펫 파라미터·템플릿을 펼쳐 초안으로 변환
GET/backups?portId=&configFile=설정 YAML 백업 목록(최신순)
POST/backups현재 설정 YAML 백업 생성 + 내용 반환
GET/backups/:filename?raw=백업 내용(JSON) 또는 YAML 다운로드
POST/backups/restore백업을 설정 파일로 복원 + 사전 백업 + 재시작
POST/bridges/delete백업이 일치할 때만 설정 YAML 삭제 + 재시작
GET/schema번들된 JSON 스키마 이름 목록
GET/schema/:schemaName설정 JSON 스키마(엔티티 필드·enum·제약)
GET/eventsSSE 실시간 이벤트 스트림
POST/entities/validate저장 없이 초안 검증/충돌/디스커버리 미리보기
POST/entities/apply초안을 YAML에 적용 + 백업 + 재시작 플래그
POST/mcpMCP Streamable HTTP (JSON-RPC 2.0, stateless)

portId는 로드된 포트가 하나뿐이면 생략할 수 있습니다. 여러 포트가 로드된 경우 생략하면 400 PORT_ID_REQUIRED가 반환됩니다.

REST 사용법 ​

아래 예시는 curl 기준입니다. H2M은 에이전트 API의 기본 URL이며 접근 환경에 맞게 변경하세요.

  • 같은 호스트: http://127.0.0.1:3001/api/v1/agent
  • LAN/원격: https://<H2M-HOST>/api/v1/agent (Agent 포트로 프록시)

초기 설정 및 브릿지 추가 ​

로드된 설정이 하나도 없으면 UI 초기 설정 마법사 대신 API로 초기 설정을 완료할 수 있습니다. 이때 /setup/apply의 mode는 init(기본값)입니다. 이미 설정이 로드된 상태에서 새 포트(브릿지)를 추가하려면 mode: "add"를 사용하며, /setup의 loadedPortIds와 겹치지 않는 portId를 지정해야 합니다. /setup/test-serial은 설정 로드 여부와 관계없이 사용할 수 있습니다.

bash
# 1. 설정 상태, 예제 목록, 사용 중인 portId 확인
curl -s -H "Authorization: Bearer $TOKEN" "$H2M/setup"

# 2. 예제의 serial 기본값과 선택 가능한 엔티티 확인
curl -s -H "Authorization: Bearer $TOKEN" "$H2M/setup/examples/bestin.homenet_bridge.yaml"

# 3. 배선 테스트 (최대 10개 패킷, 3초)
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "example": "bestin.homenet_bridge.yaml",
    "serialPath": "/dev/ttyUSB0",
    "portId": "bestin_main"
  }' "$H2M/setup/test-serial"

# 4. 초기 설정 적용 (응답 후 자동 재시작)
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "mode": "init",
    "example": "bestin.homenet_bridge.yaml",
    "serialPath": "/dev/ttyUSB0",
    "portId": "bestin_main",
    "restart": true
  }' "$H2M/setup/apply"

# 5. 설정이 이미 로드된 상태에서 새 브릿지 추가 (새 portId)
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "mode": "add",
    "example": "bestin.homenet_bridge.yaml",
    "serialPath": "/dev/ttyUSB1",
    "portId": "bestin_second",
    "restart": true
  }' "$H2M/setup/apply"
  • mode 기본값은 init입니다. 설정이 이미 로드된 상태에서 init으로 호출하면 기존 설정 보호를 위해 409 SETUP_NOT_REQUIRED가 반환됩니다.
  • mode: "add"는 새 설정 파일(예: default_2.homenet_bridge.yaml)을 생성하고 .initialized 마커는 건드리지 않습니다. portId와 serialPath가 기존 포트와 중복되면 각각 400 PORT_ID_DUPLICATE, 400 SERIAL_PATH_DUPLICATE가 반환됩니다.
  • example에 __empty__를 지정하면 빈 config로 시작합니다. 이 경우 serialConfig에 portId, path, baud_rate, data_bits, parity, stop_bits를 모두 전달해야 합니다.
  • 예제 적용은 기본적으로 serial·packet_defaults 등 설정만 가져오며, 예제의 엔티티·automation·scripts는 복사하지 않습니다. 재시작 후 갤러리(/gallery*)와 /entities/apply로 필요한 엔티티를 추가하세요.
  • 주의 (AI 에이전트): includeEntities와 selectedEntities는 UI 초기 마법사용 옵션입니다. AI 에이전트 워크플로에서는 버스에 실존하지 않는 가짜 엔티티 생성을 방지하기 위해 이 파라미터들을 절대 전달하지 마십시오. 모든 엔티티는 패킷 트래픽을 관측한 뒤 갤러리 매칭(gallery-match)으로 실제 존재가 증명된 것만 추가해야 합니다.
  • restart 기본값은 true입니다. false이면 .restart-required 플래그만 기록하고 응답 후 재시작하지 않습니다.
  • MQTT 접속 정보는 config가 아니라 환경변수/애드온 옵션이므로 API로 설정할 수 없습니다.
  • Home Assistant MQTT 애드온은 기본적으로 3001/tcp가 공개되지 않으므로, 초기 설정에 API를 사용하려면 애드온 네트워크 설정에서 포트를 공개해야 합니다.
  • 에이전트 API를 켠 뒤 처음 UI에 접속하는 화면(Docker는 비밀번호 화면의 에이전트 항목, 애드온은 전용 AI 에이전트 API 설정 화면)에서 에이전트 토큰을 발급할 수 있습니다. 발급된 토큰으로 아래 /setup 엔드포인트를 바로 호출할 수 있어, 초기 마법사 전체를 AI 에이전트에 맡길 수 있습니다.

기본 워크플로 ​

bash
export H2M="http://127.0.0.1:3001/api/v1/agent"
export TOKEN="설정 > Agent API에서 발급한 토큰"

# 1. 현재 상태 관측
curl -s -H "Authorization: Bearer $TOKEN" "$H2M/ports"
curl -s -H "Authorization: Bearer $TOKEN" "$H2M/entities"
curl -s -H "Authorization: Bearer $TOKEN" "$H2M/state"
curl -s -H "Authorization: Bearer $TOKEN" "$H2M/packets/unmatched"

# 2. 원시 패킷 캡처
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"mode":"all"}' "$H2M/capture/start"

# ... 장치 조작 후 ...

curl -s -X POST -H "Authorization: Bearer $TOKEN" "$H2M/capture/stop"
curl -s -H "Authorization: Bearer $TOKEN" "$H2M/capture/files"

# 3. 캡처한 패킷을 초안 설정으로 분석 (저장 없음)
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "portId": "default",
    "input": "AA550100020003",
    "draft": {
      "entities": {
        "sensor": [
          { "id": "my_sensor", "name": "My Sensor", "state": {}, "state_number": {} }
        ]
      }
    }
  }' "$H2M/packets/analyze"

# 4. 초안 검증 (충돌/디스커버리 미리보기)
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "portId": "default",
    "entities": {
      "sensor": [
        { "id": "my_sensor", "name": "My Sensor", "state": {}, "state_number": {} }
      ]
    },
    "discoveryPreview": true
  }' "$H2M/entities/validate"

# 5. 검증 응답의 draftHash로 적용 (재시작 포함)
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "portId": "default",
    "draftHash": "sha256:...",
    "entities": {
      "sensor": [
        { "id": "my_sensor", "name": "My Sensor", "state": {}, "state_number": {} }
      ]
    },
    "restart": true
  }' "$H2M/entities/apply"

패킷 분석 ​

POST /packets/analyze

json
{
  "portId": "default",
  "input": "AA550100020003",
  "draft": {
    "entities": { "sensor": [{ "id": "foo", "name": "Foo" }] },
    "automations": [],
    "packetDefaults": {}
  },
  "similarityThreshold": 0.8
}
  • input은 hex 문자열(공백/구분자 허용) 또는 [170, 85, ...] 정수 배열입니다.
  • draft가 있으면 활성 설정을 복제한 뒤 초안 엔티티/자동화/스크립트와 packetDefaults를 병합하고 정규화합니다. 저장은 하지 않습니다.
  • 초안 검증 오류가 있어도 분석 가능한 부분은 200으로 반환되며, draftValidationErrors에 오류가 포함됩니다.
  • 응답에는 기존 분석 결과(packets, unmatchedPackets, automationMatches, errors)와 source(active/draft), packetDefaults, draftConflicts, draftApplicable, draftValidationErrors가 포함됩니다.

엔티티 초안 검증 ​

POST /entities/validate

json
{
  "portId": "default",
  "entities": {
    "sensor": [{ "id": "foo", "name": "Foo", "state": {}, "state_number": {} }]
  },
  "automations": [],
  "scripts": [],
  "packetDefaults": {},
  "discoveryPreview": true,
  "similarityThreshold": 0.8
}

주요 응답 필드:

필드설명
valid정규화 및 설정 검증을 통과했는지
applicablevalid이면서 해결되지 않은 정확한 ID 충돌이 없는지
draftHash현재 파일 + 정규화된 초안의 SHA-256 해시
normalized정규화된 초안 엔티티/자동화/스크립트/패킷 기본값
errors검증 오류
warnings시그니처 유사 충돌 등 경고
conflictsID 충돌(kind: "id") 및 시그니처 유사 충돌(kind: "signature")
discoverydiscoveryPreview: true일 때 엔티티별 MQTT 디스커버리 {draftId, entityType, topic, payload}
restartRequired항상 true
  • 엔티티 id와 unique_id는 MQTT 토픽 안전 문자(^[A-Za-z0-9_-]+$)만 허용합니다.
  • 정확한 ID 충돌은 적용을 막습니다. 시그니처 유사 충돌은 경고이며 적용을 막지 않습니다.

엔티티 초안 적용 ​

POST /entities/apply

json
{
  "portId": "default",
  "draftHash": "sha256:...",
  "entities": { "sensor": [{ "id": "foo", "name": "Foo" }] },
  "resolutions": { "foo": "overwrite" },
  "renames": { "bar": "bar_2" },
  "restart": false
}
  • draftHash가 현재 파일/초안과 다르면 409 DRAFT_STALE을 반환합니다. 다시 검증한 뒤 적용해야 합니다.
  • 정확한 ID 충돌은 resolutions(overwrite 또는 skip) 또는 renames로 해결합니다.
  • 성공 시 YAML 백업 생성 → 설정 파일 기록 → 인메모리 설정 갱신 순서로 처리하고 restartRequired: true를 반환합니다.
  • restart: true이면 응답 전송 후 서비스가 재시작됩니다.
  • 재시작 전까지 실행 중인 브리지는 기존 디바이스만 사용합니다(라이브 등록은 지원하지 않음).

설정 백업·복원 ​

기존 설정을 수정하기 전에 에이전트가 원본 YAML을 확보해 둘 수 있습니다. 백업 파일은 설정 디렉터리의 backups/에 저장되며, 파일명은 <설정파일>.<타임스탬프>.<reason>.bak 형식입니다. reason은 영문·숫자·_·-만 허용합니다.

bash
# 1. 현재 설정 백업 생성 (응답에 YAML 본문 포함)
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"portId":"default","reason":"before_gallery_apply"}' \
  "$H2M/backups"

# 2. 백업 목록 (portId 또는 configFile로 필터)
curl -s -H "Authorization: Bearer $TOKEN" "$H2M/backups?portId=default"

# 3. 백업 내용 확인 / 원본 YAML 다운로드
curl -s -H "Authorization: Bearer $TOKEN" "$H2M/backups/<filename>"
curl -s -H "Authorization: Bearer $TOKEN" "$H2M/backups/<filename>?raw=true" -o restored.yaml

# 4. 복원 (confirm 필수, 응답 후 재시작)
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"filename":"<filename>","confirm":true,"restart":true}' \
  "$H2M/backups/restore"
  • POST /backups는 portId(또는 configFile)로 대상을 고릅니다. 둘 다 생략하면 로드된 포트가 하나일 때 그 설정을 사용합니다.
  • 응답의 backup 객체에는 filename, configFile, reason, size, createdAt, bytes, yaml이 들어 있습니다. 이 YAML을 그대로 보관하면 에이전트가 원본 사본을 유지할 수 있습니다.
  • GET /backups/:filename은 ?raw=true일 때 text/yaml 첨부 파일로 내려줍니다.
  • POST /backups/restore는 confirm: true가 없으면 400 BACKUP_CONFIRM_REQUIRED입니다. 복원 전에 현재 YAML을 pre_restore 백업으로 남기고, 백업이 유효한 homenet_bridge 설정인지 확인한 뒤 원자적으로 덮어씁니다. 로드된 설정 파일이면 인메모리 설정도 갱신하며, 기본적으로 응답 후 서비스를 재시작합니다(restart: false로 생략 가능).
  • 경로 조작·형식 오류는 400 BACKUP_FILENAME_INVALID / 400 BACKUP_TARGET_INVALID / 400 BACKUP_INVALID, 없는 백업은 404 BACKUP_NOT_FOUND입니다.

브릿지 삭제 (백업 선행 필수) ​

POST /bridges/delete는 설정 YAML을 삭제합니다. 실수로 설정을 잃지 않도록 삭제 시점의 현재 YAML과 바이트 단위로 일치하는 백업을 backupFilename으로 넘겨야만 동작합니다.

bash
# 1. 삭제할 설정의 현재 YAML 백업 (응답에 filename과 YAML 포함)
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"portId":"default","reason":"before_delete"}' \
  "$H2M/backups"

# 2. 응답의 backup.filename으로 삭제 (confirm 필수, 응답 후 재시작)
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"portId":"default","backupFilename":"<filename>","confirm":true}' \
  "$H2M/bridges/delete"
  • 대상은 portId 또는 configFile로 지정합니다. 둘 다 생략하면 로드된 포트가 하나일 때 그 설정을 사용합니다. 로드되지 않은 YAML도 configFile로 삭제할 수 있습니다.
  • confirm: true가 없으면 400 BRIDGE_DELETE_CONFIRM_REQUIRED, backupFilename이 없으면 400 BACKUP_REQUIRED, 다른 설정의 백업이면 400 BACKUP_TARGET_MISMATCH, 백업 이후 YAML이 바뀌었으면 409 BACKUP_STALE입니다.
  • 삭제 후 서비스를 재시작합니다. 로드된 설정이면 인메모리 목록에서도 제거하고, 마지막 설정을 삭제하면 .initialized 마커를 지워 초기 설정 마법사가 다시 열립니다.
  • 백업 파일 자체는 삭제하지 않으므로 GET /backups?configFile=로 계속 내려받을 수 있습니다.

패킷 미리보기/전송 ​

POST /packets/preview

json
{
  "portId": "default",
  "hex": "010203",
  "header": true,
  "footer": true,
  "checksum": true
}

POST /packets/send는 설정 > Agent API에서 패킷 전송을 허용했을 때만 동작하며, 추가로 confirm: true가 필요합니다.

json
{
  "portId": "default",
  "hex": "010203",
  "header": true,
  "footer": true,
  "checksum": true,
  "confirm": true,
  "count": 1,
  "interval": 100
}
  • count는 1~10, interval은 10~1000ms로 제한됩니다.
  • h2m-config 스킬은 전송 권한이 있어도 preview로 각 패킷의 최종 hex를 확정하고 대상·순서·간격을 사용자에게 알린 뒤 명시적 승인을 받아, 승인된 계획(1회 또는 여러 패킷 시퀀스)대로만 전송하고 전송 후 실제 기기 동작 확인을 요청합니다.
  • 옵트인이 꺼져 있으면 403 PACKET_SEND_DISABLED를 반환합니다.
  • 전송 요청은 별도의 강한 rate limit(10초당 20회)이 적용되고, 감사 로그에는 portId, 패킷 hex, 횟수만 기록됩니다(토큰 제외).
  • 잘못된 패킷을 보내면 장치가 오동작할 수 있으므로 반드시 미리보기로 확인하세요.

갤러리 스니펫 조회·확장 ​

갤러리는 벤더별 엔티티 스니펫 저장소입니다. Agent API는 GitHub을 우선으로 읽고, 도달할 수 없으면 이미지에 동봉된 gallery/ 디렉터리로 폴백합니다. 응답의 source가 remote/local 중 어느 쪽에서 왔는지 알려줍니다. 목적은 로컬 사본보다 최신 프리셋을 대조해 미매칭 패킷을 남기지 않는 것이므로, 폴백으로 받은 데이터는 오래됐을 수 있다고 보고 판단하세요.

bash
# 1. 벤더 목록 (id, requirements, 스니펫 수, 엔티티 타입)
curl -s -H "Authorization: Bearer $TOKEN" "$H2M/gallery"

# 2. 특정 벤더의 스니펫 인덱스 (parameters, content_summary, discovery 포함)
curl -s -H "Authorization: Bearer $TOKEN" "$H2M/gallery?vendor=commax"

# 3. 스니펫 원본 YAML
curl -s -H "Authorization: Bearer $TOKEN" \
  "$H2M/gallery/snippet?file=commax/heaters_new.yaml"

# 4. 관측 패킷으로 스니펫 점수화 (채널 수 같은 파라미터 추정치 반환)
curl -s -H "Authorization: Bearer $TOKEN" \
  "$H2M/gallery/match?portId=commax&vendor=commax"

# 5. 스니펫을 초안으로 펼침 (파라미터 기본값 또는 추정치 사용)
curl -s -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"file":"commax/heaters_new.yaml","parameterValues":{"heater_count":4},"portId":"commax"}' \
  "$H2M/gallery/expand"
  • file은 갤러리 상대 경로여야 합니다(commax/heaters_new.yaml). ..이나 절대 경로는 400 GALLERY_FILE_INVALID.
  • /gallery/match는 로그 리텐션이 켜져 있어야 합니다. 꺼져 있으면 available: false와 사유를 반환합니다(에러가 아닙니다).
  • /gallery/match는 벤더의 requirements(serial/checksum 등)를 만족하는 포트만 대상으로 하며, discovery가 있는 스니펫만 채점합니다. 결과는 matched, matchedPacketCount 내림차순입니다.
  • /gallery/snippet은 $repeat/ 템플릿을 펼치지 않고 원본 그대로 반환합니다. 실제 초안이 필요하면 /gallery/expand를 씁니다.
  • /gallery/expand는 parameters 기본값(또는 parameterValues 재정의)을 검증한 뒤 $repeat/$if/을 결정적으로 펼쳐 draft: { entities, automations, scripts }를 반환합니다. 스니펫의 automation(단수)은 초안 봉투에 맞춰 automations(복수)로 변환됩니다. parameterValues가 범위를 벗어나면 400 GALLERY_EXPAND_FAILED, 펼친 항목에 id가 없으면 400 GALLERY_CONTENT_INVALID입니다.
  • 반환된 draft를 그대로 /entities/validate에 넣고, 충돌·미매칭을 확인한 뒤 /entities/apply로 반영합니다. portId는 선택이며, 주어지면 로드된 포트인지 먼저 검증합니다.

설정 스키마 조회 ​

엔티티 필드명·enum·required 여부를 추측하지 않도록, 이 빌드에 동봉된 JSON 스키마를 그대로 제공합니다. 편집기(YAML 자동완성)가 쓰는 파일과 같은 원본입니다.

bash
# 1. 사용 가능한 스키마 이름
curl -s -H "Authorization: Bearer $TOKEN" "$H2M/schema"

# 2. 설정 스키마 (약 120KB)
curl -s -H "Authorization: Bearer $TOKEN" "$H2M/schema/homenet-bridge"
  • homenet-bridge 스키마의 definitions에 엔티티 타입별 정의(ClimateEntity, LightEntity, StateSchema, PacketDefaults 등)가 있습니다.
  • 응답이 크므로 필요한 정의만 골라 읽으세요. 전체를 컨텍스트에 올리는 것은 권장하지 않습니다.
  • 스키마는 Zod 정의에서 자동 생성되므로 필드 이름·enum의 정본이지만, 체크섬 알고리즘처럼 스키마에서 string으로 느슨하게 표현된 항목은 docs의 설명과 실제 동작을 함께 확인해야 합니다.
  • 저장은 하지 않는 읽기 전용 엔드포인트입니다. 최종 검증은 항상 /entities/validate가 담당합니다.

실시간 이벤트 (SSE) ​

bash
curl -N -H "Authorization: Bearer $TOKEN" \
  "$H2M/events?types=state,parsed-packet,unmatched-packet,command-packet,bridge-status"
  • types: state, parsed-packet, unmatched-packet, command-packet, bridge-status, raw (쉼표 구분)
  • portId: 특정 포트만 수신
  • raw: all 또는 valid (지정 시 raw 타입이 자동 추가됨)
  • sinceId 또는 Last-Event-ID 헤더: 최근 500개 이벤트까지 재생
  • 최초 연결 시 event: snapshot으로 포트/상태/캡처 현황을 전송합니다.
  • 이벤트는 id, event, data 형식이며 15초마다 : ping 하트비트를 보냅니다.
  • 동시 연결은 최대 8개입니다. 초과 시 429 TOO_MANY_EVENT_STREAMS를 반환합니다.
  • raw 구독 시 UI WebSocket과 공유되는 원시 패킷 리스너가 참조 카운트로 켜지고, 마지막 구독이 종료되면 자동으로 꺼집니다.

제한 및 오류 코드 ​

항목값
에이전트 API 요청 한도분당 120회 (전역 300회와 별도)
패킷 전송 한도10초당 20회
패킷 이력 최대 조회1,000건
SSE 이벤트 버퍼500건
SSE 동시 연결8개
패킷 전송 횟수최대 10회

자주 만나는 오류:

  • CONFIG_NOT_LOADED: 아직 설정이 로드되지 않음
  • PORT_NOT_FOUND / PORT_ID_REQUIRED: 포트 지정 오류
  • ENTITY_NOT_FOUND: 엔티티 없음
  • DRAFT_INVALID (422): 초안 검증 실패
  • DRAFT_STALE (409): 검증 후 파일/초안이 변경됨
  • DRAFT_CONFLICT (409): 해결되지 않은 ID 충돌
  • GALLERY_UNAVAILABLE (503): GitHub와 동봉 gallery/ 모두에서 갤러리 목록을 읽지 못함
  • GALLERY_VENDOR_NOT_FOUND (404): 갤러리에 없는 벤더 id
  • GALLERY_FILE_INVALID (400) / GALLERY_FILE_NOT_FOUND (404): 스니펫 경로 오류
  • GALLERY_PARAMETER_INVALID (400): parameterValues가 객체가 아님
  • GALLERY_EXPAND_FAILED (400): 파라미터 누락·범위 초과 또는 템플릿 평가 실패
  • GALLERY_CONTENT_INVALID (400): 펼친 엔티티/automation/script에 id가 없음
  • GALLERY_YAML_INVALID (400) / GALLERY_VERSION_INCOMPATIBLE (400): 스니펫 YAML 파싱 실패 또는 meta.min_version 미충족
  • SCHEMA_NAME_INVALID (400) / SCHEMA_NOT_FOUND (404): 스키마 이름 오류(^[a-z0-9-]+$만 허용)

기존 API와의 관계 ​

  • 기존 /api/*와 /api/stream(WebSocket)은 번들 UI를 위해 그대로 유지됩니다.
  • /api/*는 UI 세션 쿠키(또는 신뢰된 Home Assistant ingress 요청)로 보호됩니다. 예전처럼 무인증이 아니므로, /api/schema 같은 UI 경로를 에이전트가 직접 호출할 수는 없습니다. 에이전트는 같은 정보를 /api/v1/agent/schema에서 Bearer 토큰으로 받습니다.
  • 에이전트 API는 기존 UI API와 같은 서비스/설정 로직을 공유하므로, 두 경로에서 동시에 설정을 수정하면 포트별 쓰기 락으로 직렬화됩니다.

MCP 사용법 ​

REST의 주요 기능을 MCP Streamable HTTP 도구로도 제공합니다. MCP는 REST를 대체하지 않으며, REST 라우트와 동일한 agent/services/* 도메인 로직을 호출하는 얇은 어댑터입니다.

MCP 클라이언트는 일반적으로 다음 순서로 연결합니다.

  1. initialize로 프로토콜 버전과 서버 정보를 교환합니다.
  2. notifications/initialized를 보냅니다(응답 본문 없음).
  3. tools/list로 사용 가능한 도구를 확인합니다.
  4. tools/call로 도구를 실행합니다.
  • 엔드포인트: POST /api/v1/agent/mcp
  • 전송: Streamable HTTP, JSON-RPC 2.0, stateless(세션 ID 없음, GET/DELETE는 405)
  • 인증: REST와 동일한 Authorization: Bearer <AGENT_TOKEN> 헤더
  • Content-Type: application/json 필수
  • 지원 메서드: initialize, notifications/initialized(202, 본문 없음), ping, tools/list, tools/call
  • 지원 프로토콜 버전: 2025-06-18, 2025-03-26, 2024-11-05

도구 목록 ​

도구설명
get_setup_state설정 상태, 예제 목록, 사용 중인 portId
get_setup_example예제의 serial/packet 기본값과 선택 엔티티
test_setup_serialserial 배선 테스트 (설정 로드 중에도 가능)
apply_initial_setup초기 설정(mode="init") 또는 브릿지 추가(mode="add") + 재시작
list_ports로드된 포트/브리지 상태/엔티티 수
get_entities정규화된 엔티티 목록 (portId 선택)
get_automations정규화된 automation 목록 (portId 선택)
get_scripts정규화된 script 목록 (portId 선택)
get_entity_state최신 엔티티 상태 스냅샷
get_packet_historyparsed/command 통합 패킷 이력
get_unmatched_packets미매칭 패킷 + 횟수/최초·최근 시각
analyze_packet활성 또는 초안 설정으로 패킷 분석
start_capture / stop_capture원시 패킷 캡처 제어
get_capture_status / list_capture_files캡처 상태와 저장 파일 목록
validate_entity_draft저장 없이 초안 검증 + 충돌/디스커버리 미리보기
apply_entity_draftdraftHash로 초안을 YAML에 적용 + 백업 + 재시작 플래그
get_recent_eventsafterId 기반 이벤트 폴링 (최근 500건 버퍼, SSE 대체)
list_gallery갤러리 벤더 목록 또는 벤더별 스니펫 인덱스 (vendor 선택)
get_gallery_snippet갤러리 스니펫 원본 YAML (file 필수)
match_gallery관측 패킷으로 갤러리 스니펫 채점 + 파라미터 추정
expand_gallery_snippet스니펫을 파라미터·템플릿 기준으로 펼쳐 초안 반환 (file 필수)
list_backups설정 YAML 백업 목록 (portId/configFile 선택)
create_backup현재 설정 YAML 백업 생성 + 메타데이터/YAML 반환
get_backup백업 YAML 다운로드 (filename 필수)
restore_backup백업 복원 + 사전 백업 + 재시작 (filename, confirm 필수)
delete_bridge설정 YAML 삭제 (backupFilename, confirm 필수, 백업 일치 필요)
list_config_schemas동봉된 설정 스키마 이름 목록
get_config_schema설정 JSON 스키마 (엔티티 필드·enum·required, 약 120KB)
send_packetRS485 전송. UI에서 패킷 전송을 허용한 경우에만 tools/list에 노출

send_packet이 비활성인 상태에서 직접 호출하면 isError: true와 PACKET_SEND_DISABLED가 반환됩니다. 적용 계열 도구는 REST와 동일하게 YAML 백업과 초안 해시 검증을 수행합니다. restore_backup은 confirm: true 없이는 실행되지 않으며, 기본적으로 복원 후 서비스를 재시작합니다(restart: false로 생략). delete_bridge도 confirm: true와 현재 YAML과 일치하는 backupFilename이 필요하고, 삭제 후 서비스를 재시작합니다.

apply_initial_setup은 설정이 이미 로드된 상태에서 mode="init"이면 isError: true와 SETUP_NOT_REQUIRED를 반환합니다. 새 브릿지는 mode="add"와 get_setup_state의 loadedPortIds에 없는 portId로 추가하세요. 예제 적용은 기본적으로 설정만 복사하며, 에이전트 워크플로에서는 includeEntities나 selectedEntities를 사용하지 않습니다(가짜 엔티티 방지). test_setup_serial은 설정 로드 여부와 관계없이 동작합니다.

캡처 파일 다운로드/삭제, 전송 패킷 미리보기, 백업 파일 삭제는 MCP 도구로 제공되지 않으므로 REST 또는 UI를 사용하세요.

응답 형식 ​

tools/call 성공 시 content(텍스트)와 structuredContent(JSON)가 함께 반환됩니다.

json
{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "content": [{ "type": "text", "text": "{ ... }" }],
    "structuredContent": { "unmatchedPackets": [] }
  }
}

도구 실행 오류는 JSON-RPC 오류가 아니라 result.isError: true로 반환됩니다.

json
{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "isError": true,
    "content": [{ "type": "text", "text": "{\"error\":\"PORT_NOT_FOUND\",\"message\":\"...\"}" }],
    "structuredContent": { "error": "PORT_NOT_FOUND", "message": "..." }
  }
}

curl 예시 ​

bash
# 1. 초기화
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}}}' \
  "$H2M/mcp"

# 2. 초기화 완료 알림 (202, 본문 없음)
curl -s -o /dev/null -w '%{http_code}\n' -X POST -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","method":"notifications/initialized"}' \
  "$H2M/mcp"

# 3. 도구 목록
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
  "$H2M/mcp"

# 4. 도구 호출
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"get_unmatched_packets","arguments":{}}}' \
  "$H2M/mcp"

# 5. 초안 검증 → 적용
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"validate_entity_draft","arguments":{"discoveryPreview":true,"entities":{"sensor":[{"id":"my_sensor","name":"My Sensor","state":{},"state_number":{}}]}}}}' \
  "$H2M/mcp"

curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"apply_entity_draft","arguments":{"draftHash":"sha256:...","entities":{"sensor":[{"id":"my_sensor","name":"My Sensor","state":{},"state_number":{}}]},"restart":true}}}' \
  "$H2M/mcp"

클라이언트 연결 ​

MCP Streamable HTTP를 지원하는 클라이언트에서 URL과 인증 헤더를 등록합니다.

Cursor (mcp.json)

json
{
  "mcpServers": {
    "homenet2mqtt": {
      "url": "http://127.0.0.1:3001/api/v1/agent/mcp",
      "headers": {
        "Authorization": "Bearer <AGENT_TOKEN>"
      }
    }
  }
}

stdio 전용 클라이언트 (mcp-remote 브리지)

json
{
  "mcpServers": {
    "homenet2mqtt": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "http://127.0.0.1:3001/api/v1/agent/mcp",
        "--header",
        "Authorization: Bearer <AGENT_TOKEN>"
      ]
    }
  }
}

클라이언트 설정의 URL은 접근 환경에 맞게 변경하세요.

  • 같은 호스트: http://127.0.0.1:3001/api/v1/agent/mcp
  • 같은 Docker 네트워크: http://<컨테이너명>:3001/api/v1/agent/mcp
  • LAN/원격: https://<H2M-HOST>/api/v1/agent/mcp (Agent 포트로 리버스 프록시 권장)
  • Matter 애드온: http://<HA-IP>:3001/api/v1/agent/mcp
  • MQTT 애드온: ingress는 외부 MCP 클라이언트용이 아니므로 3001/tcp 포트 공개 또는 리버스 프록시가 필요합니다.

위 예시는 같은 호스트에서 실행하는 경우를 기준으로 합니다. 원격 서버라면 127.0.0.1 대신 실제 호스트 이름/IP를 사용하세요.

오류 매핑 ​

  • 도구 실행 오류는 JSON-RPC 오류가 아니라 result.isError: true와 { "error": "...", "message": "..." } 구조로 반환됩니다.
  • 알 수 없는 도구/잘못된 params는 JSON-RPC -32602, 알 수 없는 메서드는 -32601, 잘못된 요청은 HTTP 400 + -32600입니다.
  • 인증, rate limit, 초안 해시/충돌 규칙은 REST와 동일합니다.

관련 문서 ​