{
  "pluginAlias": "SmartThingsKM81",
  "pluginType": "platform",
  "singular": true,
  "headerDisplay": "Samsung 기기(구형 에어컨 / 신형 에어컨 / 세탁기·건조기)를 하나의 플랫폼으로 통합 관리합니다. SmartThings를 사용하는 장치(신형·시스템 AC / 세탁기 / 건조기)가 하나라도 있으면 Client ID/Secret/Redirect URI가 필요합니다.",
  "schema": {
    "type": "object",
    "properties": {
      "name": {
        "title": "플랫폼 이름",
        "type": "string",
        "default": "SmartThings KM81",
        "required": true
      },
      "clientId": {
        "title": "SmartThings Client ID",
        "type": "string",
        "description": "SmartThings Developer Workspace에서 발급받은 Client ID (SmartThings 기반 장치를 사용할 때만 필수)"
      },
      "clientSecret": {
        "title": "SmartThings Client Secret",
        "type": "string"
      },
      "redirectUri": {
        "title": "Redirect URI",
        "type": "string",
        "description": "SmartThings 앱 등록 시 넣은 리디렉션 주소와 같아야 합니다.",
        "placeholder": "https://myhome.example.com"
      },
      "temperatureMin": {
        "title": "기본 목표온도 최소값(℃) — SmartAC 공용",
        "type": "number",
        "default": 18
      },
      "temperatureMax": {
        "title": "기본 목표온도 최대값(℃) — SmartAC 공용",
        "type": "number",
        "default": 30
      },
      "devices": {
        "type": "array",
        "title": "장치 목록",
        "default": [],
        "items": {
          "type": "object",
          "properties": {
            "deviceType": {
              "title": "장치 종류",
              "type": "string",
              "default": "smartAc",
              "oneOf": [
                {
                  "title": "구형 에어컨",
                  "enum": [
                    "legacyAc"
                  ]
                },
                {
                  "title": "신형 에어컨",
                  "enum": [
                    "smartAc"
                  ]
                },
                {
                  "title": "시스템 에어컨",
                  "enum": [
                    "systemAc"
                  ]
                },
                {
                  "title": "세탁기",
                  "enum": [
                    "washer"
                  ]
                },
                {
                  "title": "건조기",
                  "enum": [
                    "dryer"
                  ]
                },
                {
                  "title": "정수기 (홈킷에 안 뜸 — HA로만 중계)",
                  "enum": [
                    "waterPurifier"
                  ]
                }
              ],
              "required": true,
              "description": "구형 = 2in1 등 SmartThings 이전 모델(직접 통신). 그 외는 SmartThings 기기. 신형과 시스템 에어컨은 설정 항목이 같습니다."
            },
            "name": {
              "title": "[구형 AC] 액세서리 이름 (필수)",
              "type": "string",
              "description": "홈 앱에 표시될 이름"
            },
            "ip": {
              "title": "[구형 AC] 에어컨 IP 주소 (필수)",
              "type": "string",
              "format": "ipv4"
            },
            "token": {
              "title": "[구형 AC] 인증 토큰 (필수)",
              "type": "string",
              "description": "기기가 발급하는 토큰입니다. README의 \"기기 토큰 추출하기\" 절을 따라 1회만 받으면 계속 쓸 수 있습니다."
            },
            "deviceIndex": {
              "title": "[구형 AC] 장치 인덱스 (읽기)",
              "type": "integer",
              "default": 0,
              "description": "기기가 상태를 보고할 때의 순서(0부터). 2in1(실외기 1대에 실내기 2대)이면 같은 IP로 항목을 2개 만들고 방마다 0과 1을 나눠 씁니다. 단일 기기는 0."
            },
            "setDeviceIndex": {
              "title": "[구형 AC] 장치 인덱스 (쓰기)",
              "type": "integer",
              "description": "명령을 보낼 때의 순서입니다. 비우면 읽기와 같은 값을 씁니다. 2in1 기기 중에는 읽기와 쓰기 순서가 다른 모델이 있습니다 — 제어가 안 되거나 다른 방이 켜지면 두 값을 서로 바꿔 보세요."
            },
            "pollingInterval": {
              "title": "[AC 공통] 상태 폴링 간격 (초)",
              "type": "integer",
              "description": "기기 상태를 다시 읽는 주기. 비워두면 신형 30초 / 구형은 폴링 안 함."
            },
            "timeout": {
              "title": "[구형 AC] 요청 시간 초과 (ms)",
              "type": "integer",
              "default": 5000,
              "description": "(구버전 호환용, 밀리초) 위의 초 단위 항목을 쓰세요. 초 값이 있으면 그쪽이 우선합니다."
            },
            "cacheDuration": {
              "title": "[구형 AC] 캐시 유지 시간 (ms)",
              "type": "integer",
              "default": 30000,
              "description": "(구버전 호환용, 밀리초) 위의 초 단위 항목을 쓰세요. 초 값이 있으면 그쪽이 우선합니다."
            },
            "certPath": {
              "title": "[구형 AC] 인증서 경로 (선택)",
              "type": "string",
              "description": "비워두면 패키지에 포함된 cert/cert.pem을 사용합니다."
            },
            "keyPath": {
              "title": "[구형 AC] 키 파일 경로 (선택)",
              "type": "string"
            },
            "manufacturer": {
              "title": "[구형 AC] 제조사",
              "type": "string",
              "default": "Samsung"
            },
            "debug": {
              "title": "[구형 AC] 상세 로그",
              "type": "boolean",
              "default": false
            },
            "minTemp": {
              "title": "[구형 AC] 목표온도 최소값 (°C)",
              "type": "integer",
              "default": 18
            },
            "maxTemp": {
              "title": "[구형 AC] 목표온도 최대값 (°C)",
              "type": "integer",
              "default": 30
            },
            "hkCoolMode": {
              "title": "[구형 AC] 냉방 버튼 → 보낼 모드",
              "type": "string",
              "default": "Cool",
              "oneOf": [
                {
                  "title": "냉방",
                  "enum": [
                    "Cool"
                  ]
                },
                {
                  "title": "냉방청정",
                  "enum": [
                    "CoolClean"
                  ]
                },
                {
                  "title": "제습",
                  "enum": [
                    "Dry"
                  ]
                },
                {
                  "title": "제습청정",
                  "enum": [
                    "DryClean"
                  ]
                },
                {
                  "title": "송풍",
                  "enum": [
                    "Wind"
                  ]
                },
                {
                  "title": "자동",
                  "enum": [
                    "Auto"
                  ]
                }
              ],
              "description": "HomeKit '냉방'을 눌렀을 때 구형 에어컨으로 보낼 모드. 송풍·자동은 냉방을 하지 않으므로 온도 설정이 의미 없을 수 있습니다."
            },
            "legacySwingBinding": {
              "title": "[구형 AC] 스윙(Swing) 토글 ↔ 기능",
              "type": "string",
              "default": "comfort",
              "oneOf": [
                {
                  "title": "무풍",
                  "enum": [
                    "comfort"
                  ]
                },
                {
                  "title": "회전",
                  "enum": [
                    "wind"
                  ]
                },
                {
                  "title": "사용 안 함",
                  "enum": [
                    "none"
                  ]
                }
              ],
              "description": "구형 에어컨은 상하 바람만 켜고 끌 수 있습니다(방향 선택 없음)."
            },
            "legacyLockBinding": {
              "title": "[구형 AC] 어린이 보호용 잠금장치 ↔ 기능",
              "type": "string",
              "default": "autoClean",
              "oneOf": [
                {
                  "title": "자동건조",
                  "enum": [
                    "autoClean"
                  ]
                },
                {
                  "title": "사용 안 함",
                  "enum": [
                    "none"
                  ]
                }
              ],
              "description": "홈킷 잠금 토글에 연결할 기능. '사용 안 함'이면 토글이 사라집니다."
            },
            "legacyOnGuardMs": {
              "title": "[구형 AC] 전원 ON 직후 보호 시간 (ms)",
              "type": "integer",
              "default": 2000,
              "description": "(구버전 호환용, 밀리초) 위의 초 단위 항목을 쓰세요. 초 값이 있으면 그쪽이 우선합니다."
            },
            "legacyOnGuardStrategy": {
              "title": "[구형 AC] ON 보호 중 다른 명령 처리",
              "type": "string",
              "default": "drop",
              "oneOf": [
                {
                  "title": "무시",
                  "enum": [
                    "drop"
                  ]
                },
                {
                  "title": "끝난 뒤 전송",
                  "enum": [
                    "queue"
                  ]
                }
              ],
              "description": "보호 시간 중 들어온 명령을 버릴지, 끝난 뒤 보낼지."
            },
            "resendModeOnPowerOn": {
              "title": "[AC 공통] 전원 ON 후 설정 모드 자동 재전송",
              "type": "boolean",
              "default": false,
              "description": "켤 때마다 설정 모드를 한 번 더 보냅니다(기기가 첫 명령을 놓치는 경우 대비)."
            },
            "resendAutoCleanOnPowerOn": {
              "title": "[AC 공통] 전원 ON 후 자동건조 자동 켜기",
              "type": "boolean",
              "default": false,
              "description": "켤 때마다 자동건조를 켭니다."
            },
            "resendSwingOffOnPowerOn": {
              "title": "[구형 AC] 전원 ON 후 스윙(회전) 자동 끄기",
              "type": "boolean",
              "default": false,
              "description": "켤 때마다 스윙을 끕니다."
            },
            "powerOnResendStepMs": {
              "title": "[AC 공통] 후속 재전송 단계 간격 (ms)",
              "type": "integer",
              "default": 2000,
              "description": "(구버전 호환용, 밀리초) 위의 초 단위 항목을 쓰세요. 초 값이 있으면 그쪽이 우선합니다."
            },
            "hideSwingToggle": {
              "title": "[구형 AC] 스윙 토글 HomeKit에서 숨기기",
              "type": "boolean",
              "default": false,
              "description": "스윙 토글을 숨깁니다."
            },
            "hideLockToggle": {
              "title": "[구형 AC] 잠금 토글 숨기기",
              "type": "boolean",
              "default": false,
              "description": "잠금 토글을 숨깁니다."
            },
            "stateDumpFile": {
              "title": "[구형 AC] 상태 파일 덤프 경로 (선택)",
              "type": "string",
              "description": "기기 상태를 이 파일로 내보냅니다(HA 등 외부에서 읽기용)."
            },
            "deviceLabel": {
              "title": "[SmartThings] 장치 이름 (기기 IP를 적었으면 비워도 됨)",
              "type": "string",
              "description": "SmartThings 앱에 등록된 이름과 정확히 같아야 합니다. 이름으로 찾을 때는 OAuth 연결이 필요합니다. 전송 경로가 로컬이고 기기 IP를 적었다면 비워 둬도 됩니다."
            },
            "model": {
              "title": "모델명 (선택)",
              "type": "string"
            },
            "serialNumber": {
              "title": "일련번호 (선택)",
              "type": "string"
            },
            "coolModeCommand": {
              "title": "[신형·시스템 AC] 냉방 버튼 → 보낼 모드",
              "type": "string",
              "default": "cool",
              "oneOf": [
                {
                  "title": "냉방",
                  "enum": [
                    "cool"
                  ]
                },
                {
                  "title": "제습",
                  "enum": [
                    "dry"
                  ]
                },
                {
                  "title": "송풍",
                  "enum": [
                    "fan"
                  ]
                },
                {
                  "title": "AI 쾌적",
                  "enum": [
                    "aIComfort"
                  ]
                },
                {
                  "title": "자동",
                  "enum": [
                    "auto"
                  ]
                }
              ],
              "description": "HomeKit '냉방'을 눌렀을 때 기기로 보낼 모드."
            },
            "swingBinding": {
              "title": "[신형 AC] 스윙(Swing) 토글 ↔ 기능",
              "type": "string",
              "default": "windFree",
              "oneOf": [
                {
                  "title": "무풍",
                  "enum": [
                    "windFree"
                  ]
                },
                {
                  "title": "회전",
                  "enum": [
                    "rotate"
                  ]
                },
                {
                  "title": "사용 안 함",
                  "enum": [
                    "none"
                  ]
                }
              ],
              "description": "스윙 토글이 무엇을 켜고 끌지 고릅니다. 회전은 기기가 지원한다고 알려준 방향을 자동으로 씁니다(로컬 경로 전용)."
            },
            "lockBinding": {
              "title": "[신형·시스템 AC] 어린이 보호용 잠금장치 ↔ 기능",
              "type": "string",
              "default": "autoClean",
              "oneOf": [
                {
                  "title": "자동건조",
                  "enum": [
                    "autoClean"
                  ]
                },
                {
                  "title": "사용 안 함",
                  "enum": [
                    "none"
                  ]
                }
              ]
            },
            "exposeWindFreeSwitch": {
              "title": "[신형·시스템 AC] 무풍 별도 스위치 노출",
              "type": "boolean",
              "default": false,
              "description": "무풍을 별도 스위치로도 노출합니다."
            },
            "exposeAutoCleanSwitch": {
              "title": "[신형·시스템 AC] 자동건조 별도 스위치 노출",
              "type": "boolean",
              "default": false,
              "description": "자동건조를 별도 스위치로도 노출합니다."
            },
            "temperatureMin": {
              "title": "[신형·시스템 AC] 목표온도 최소값 (°C)",
              "type": "number"
            },
            "temperatureMax": {
              "title": "[신형·시스템 AC] 목표온도 최대값 (°C)",
              "type": "number"
            },
            "enableNotificationSensor": {
              "title": "[세탁/건조기] 종료 알림 센서 활성화",
              "type": "boolean",
              "default": false,
              "description": "운전이 끝나면 홈킷 알림을 보낼 모션 센서를 만듭니다."
            },
            "sensorName": {
              "title": "[세탁/건조기] 종료 알림 센서 이름",
              "type": "string",
              "description": "비워두면 '<장치 이름> 종료알림'."
            },
            "sensorPollInterval": {
              "title": "[세탁/건조기] 상태 폴링 간격 (초)",
              "type": "integer",
              "default": 30,
              "minimum": 5,
              "description": "운전 종료를 감지하는 확인 주기."
            },
            "mqttSlug": {
              "title": "MQTT 토픽 이름",
              "type": "string",
              "required": false,
              "description": "Home Assistant 중계에 쓸 토픽 이름입니다. 비우면 기기 종류를 씁니다. 한 번 정한 뒤 바꾸면 Home Assistant에 엔티티가 새로 생기고 기존 것은 남으니, 처음에 정해 두는 편이 좋습니다.",
              "placeholder": "seungjun_ac",
              "pattern": "^[a-z0-9_]+$"
            },
            "mqttExpose": {
              "title": "HA(MQTT)로 중계",
              "type": "boolean",
              "default": true,
              "required": false,
              "description": "끄면 이 기기를 Home Assistant로 중계하지 않습니다. 기본은 켬입니다."
            },
            "transport": {
              "title": "[SmartThings] 전송 경로",
              "type": "string",
              "default": "cloud",
              "oneOf": [
                {
                  "title": "클라우드",
                  "enum": [
                    "cloud"
                  ]
                },
                {
                  "title": "로컬",
                  "enum": [
                    "local"
                  ]
                }
              ],
              "condition": {
                "functionBody": "return model.devices[arrayIndices[0]] && ['smartAc','systemAc','washer','dryer'].includes(model.devices[arrayIndices[0]].deviceType);"
              },
              "description": "로컬 = 같은 네트워크에서 기기와 직접 통신합니다.",
              "required": true
            },
            "local": {
              "title": "로컬 전송 설정",
              "type": "object",
              "properties": {
                "host": {
                  "title": "[SmartThings] 기기 IP (전송 경로가 로컬이면 필수)",
                  "type": "string",
                  "placeholder": "192.168.1.78",
                  "required": false,
                  "description": "공유기에서 고정 IP로 잡아두세요. 포트는 자동으로 찾습니다. 비워 두면 로컬로 붙지 못하고 클라우드로 동작합니다."
                },
                "port": {
                  "title": "DTLS 포트 (선택)",
                  "type": "integer",
                  "description": "(고급) 비워두면 자동 탐지.",
                  "placeholder": "비워두면 자동 탐지"
                },
                "localPort": {
                  "title": "고정 송신 포트 (선택)",
                  "type": "integer",
                  "description": "(고급) 비워두면 기기 IP로부터 자동 결정."
                },
                "fallbackToCloud": {
                  "title": "[SmartThings] 로컬 실패 시 클라우드 사용",
                  "type": "boolean",
                  "default": true,
                  "description": "로컬이 실패하면 클라우드로 대신 처리합니다. 끄면 SmartThings API를 쓰지 않으며, 로컬이 실패하면 그 기기는 제어되지 않습니다(홈 앱에 '응답 없음')."
                },
                "token": {
                  "title": "[세탁기] 기기 토큰",
                  "type": "string",
                  "description": "세탁기가 발급하는 토큰. README \"기기 토큰 추출하기\" 절 참고(1회). 비워두면 클라우드로 동작합니다."
                }
              }
            },
            "deviceId": {
              "title": "SmartThings deviceId (비워도 됨 — 자동으로 찾습니다)",
              "type": "string",
              "placeholder": "3ea2a924-ec77-9f1e-8d4f-1900d3f3e791",
              "description": "비워 두면 장치 이름으로 찾거나, 로컬 기기라면 부팅할 때 기기에서 직접 읽습니다. 적어 두면 부팅이 빨라집니다.",
              "condition": {
                "functionBody": "return model.devices[arrayIndices[0]] && ['smartAc','systemAc','washer','dryer'].includes(model.devices[arrayIndices[0]].deviceType);"
              }
            },
            "powerOnResendStepSec": {
              "title": "[AC 공통] 후속 재전송 간격 (초)",
              "type": "number",
              "placeholder": "2",
              "description": "전원 ON 후속 체인(모드 → 자동건조)의 단계 간격. 비워두면 2초."
            },
            "legacyOnGuardSec": {
              "title": "[구형 AC] 전원 ON 직후 보호 시간 (초)",
              "type": "number",
              "placeholder": "2",
              "description": "켠 직후 이 시간 동안 다른 명령을 무시합니다. 비워두면 2초."
            },
            "timeoutSec": {
              "title": "[구형 AC] 요청 시간 초과 (초)",
              "type": "number",
              "placeholder": "5",
              "description": "기기 응답 대기 최대 시간. 비워두면 5초."
            },
            "cacheDurationSec": {
              "title": "[구형 AC] 상태 캐시 유지 (초)",
              "type": "number",
              "placeholder": "30",
              "description": "이 시간 안에는 마지막 값을 재사용합니다. 비워두면 30초."
            },
            "splitCompartments": {
              "title": "[세탁기] 세탁조를 따로 표시",
              "type": "boolean",
              "default": false,
              "description": "2-in-1 세탁기를 홈킷에 두 개로 나눕니다. 끄면 하나로 합칩니다."
            },
            "mainCompartmentName": {
              "title": "[세탁기] 메인 세탁조 이름",
              "type": "string",
              "placeholder": "세탁기 메인",
              "description": "비워두면 \"<장치 이름> 메인\"."
            },
            "subCompartmentName": {
              "title": "[세탁기] 보조 세탁조 이름",
              "type": "string",
              "placeholder": "세탁기 보조",
              "description": "비워두면 \"<장치 이름> 보조\"."
            },
            "systemSwingBinding": {
              "title": "[시스템 AC] 스윙(Swing) 토글 ↔ 기능",
              "type": "string",
              "default": "windFree",
              "description": "스윙 토글이 무엇을 켜고 끌지 고릅니다. 끄면 항상 고정(Fix)을 보냅니다. 기기가 지원하지 않는 방향을 고르면 로그가 지원 목록과 함께 알려 줍니다(로컬 경로 전용).",
              "oneOf": [
                {
                  "title": "무풍",
                  "enum": [
                    "windFree"
                  ]
                },
                {
                  "title": "상하좌우",
                  "enum": [
                    "All"
                  ]
                },
                {
                  "title": "상하 바람",
                  "enum": [
                    "Up_And_Low"
                  ]
                },
                {
                  "title": "좌우 바람",
                  "enum": [
                    "Left_And_Right"
                  ]
                },
                {
                  "title": "사용 안 함",
                  "enum": [
                    "none"
                  ]
                }
              ]
            }
          },
          "required": [
            "deviceType"
          ]
        }
      },
      "localPythonBin": {
        "title": "(고급) 파이썬 실행 파일",
        "type": "string",
        "placeholder": "python3",
        "description": "신형 기기(DTLS) 로컬 제어에 쓰는 파이썬 실행 경로. 비우면 python3를 씁니다. ★파이썬 3.11 이상이 필요합니다 — 낮으면 여기에 새 파이썬 경로를 지정하세요."
      },
      "localStateDir": {
        "title": "(고급) 로컬 전송 자산 경로",
        "type": "string",
        "description": "신형 기기용 파이썬 자산을 둘 폴더. 비우면 홈브릿지 저장 경로 아래에 만듭니다. 설치가 권한 오류로 실패하면 쓰기 가능한 경로를 여기에 지정하세요."
      },
      "mqtt": {
        "type": "object",
        "title": "MQTT 브리지 (Home Assistant 중계)",
        "description": "로컬로 제어 중인 기기의 상태를 MQTT로 내보내 Home Assistant가 자동으로 엔티티를 만들게 합니다. HA는 기기에 직접 붙지 않습니다.",
        "properties": {
          "enabled": {
            "title": "MQTT 브리지 사용",
            "type": "boolean",
            "default": false,
            "description": "끄면 아무 것도 하지 않습니다. 홈킷 동작에는 영향이 없습니다."
          },
          "host": {
            "title": "브로커 주소",
            "type": "string",
            "placeholder": "192.168.1.11",
            "description": "Mosquitto 등 MQTT 브로커의 IP 또는 호스트 이름입니다."
          },
          "port": {
            "title": "브로커 포트",
            "type": "integer",
            "default": 1883,
            "minimum": 1,
            "maximum": 65535
          },
          "username": {
            "title": "사용자 이름",
            "type": "string"
          },
          "password": {
            "title": "비밀번호",
            "type": "string"
          },
          "baseTopic": {
            "title": "기본 토픽",
            "type": "string",
            "default": "km81/appliance",
            "description": "상태는 이 아래 <기기>/state 로 발행됩니다."
          },
          "discoveryPrefix": {
            "title": "HA 자동 검색 접두어",
            "type": "string",
            "default": "homeassistant",
            "description": "Home Assistant의 MQTT 자동 검색 접두어입니다. 기본값을 쓰면 됩니다."
          },
          "republishInterval": {
            "title": "재발행 주기(초)",
            "type": "integer",
            "default": 60,
            "minimum": 15,
            "maximum": 3600,
            "description": "변화가 없어도 이 주기로 현재 상태를 다시 올려, 브로커가 재시작해도 값이 비지 않게 합니다."
          }
        }
      }
    },
    "required": [
      "name",
      "devices"
    ]
  },
  "layout": [
    "name",
    {
      "type": "fieldset",
      "title": "SmartThings 인증 (신형·시스템 AC / 세탁기 / 건조기 사용 시 필수)",
      "expandable": true,
      "expanded": false,
      "items": [
        {
          "type": "help",
          "help": "SmartThings 개발자 워크스페이스 또는 CLI(<code>smartthings apps:create</code>)로 <b>OAuth-In</b> 앱을 만듭니다.<br>· 권한(Scope) <b>세 개 모두</b>: <code>r:devices:*</code> <code>w:devices:*</code> <code>x:devices:*</code><br>· Redirect URI는 <b>https 도메인만</b> 받습니다(포트 불가). 예: <code>https://myhome.example.com/oauth/callback</code><br>· 그 주소가 리버스 프록시를 거쳐 <b>홈브릿지의 8999 포트</b>에 닿아야 합니다.<br>· 토큰 파일: <code>smartthings_km81_token.json</code><br><b>그림과 함께 보는 자세한 절차는 README의 \"SmartThings 클라우드를 쓸 때\" 절을 보세요.</b>"
        },
        "clientId",
        "clientSecret",
        "redirectUri"
      ]
    },
    {
      "type": "fieldset",
      "title": "SmartAC 공용 기본값 (개별 장치에서 덮어쓸 수 있음)",
      "expandable": true,
      "expanded": false,
      "items": [
        "temperatureMin",
        "temperatureMax"
      ]
    },
    {
      "type": "fieldset",
      "title": "MQTT 브리지 (Home Assistant 중계)",
      "expandable": true,
      "expanded": false,
      "items": [
        "mqtt.enabled",
        {
          "key": "mqtt.host",
          "condition": {
            "functionBody": "return model.mqtt && model.mqtt.enabled === true;"
          }
        },
        {
          "key": "mqtt.port",
          "condition": {
            "functionBody": "return model.mqtt && model.mqtt.enabled === true;"
          },
          "type": "number"
        },
        {
          "key": "mqtt.username",
          "condition": {
            "functionBody": "return model.mqtt && model.mqtt.enabled === true;"
          }
        },
        {
          "key": "mqtt.password",
          "condition": {
            "functionBody": "return model.mqtt && model.mqtt.enabled === true;"
          },
          "type": "password"
        },
        {
          "key": "mqtt.baseTopic",
          "condition": {
            "functionBody": "return model.mqtt && model.mqtt.enabled === true;"
          }
        },
        {
          "key": "mqtt.discoveryPrefix",
          "condition": {
            "functionBody": "return model.mqtt && model.mqtt.enabled === true;"
          }
        },
        {
          "key": "mqtt.republishInterval",
          "condition": {
            "functionBody": "return model.mqtt && model.mqtt.enabled === true;"
          },
          "type": "number"
        }
      ]
    },
    {
      "key": "devices",
      "type": "tabarray",
      "title": "{{ value.name || value.deviceLabel || ('Device ' + ($index + 1)) }}",
      "expandable": false,
      "items": [
        "devices[].deviceType",
        {
          "key": "devices[].name",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && model.devices[arrayIndices[0]].deviceType==='legacyAc';"
          }
        },
        {
          "key": "devices[].ip",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && model.devices[arrayIndices[0]].deviceType==='legacyAc';"
          }
        },
        {
          "key": "devices[].token",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && model.devices[arrayIndices[0]].deviceType==='legacyAc';"
          }
        },
        {
          "key": "devices[].deviceIndex",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && model.devices[arrayIndices[0]].deviceType==='legacyAc';"
          }
        },
        {
          "key": "devices[].setDeviceIndex",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && model.devices[arrayIndices[0]].deviceType==='legacyAc';"
          }
        },
        {
          "key": "devices[].deviceLabel",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && ['smartAc','systemAc','washer','dryer','waterPurifier'].includes(model.devices[arrayIndices[0]].deviceType);"
          }
        },
        {
          "key": "devices[].deviceId",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && ['smartAc','systemAc','washer','dryer','waterPurifier'].includes(model.devices[arrayIndices[0]].deviceType);"
          }
        },
        {
          "key": "devices[].pollingInterval",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && (model.devices[arrayIndices[0]].deviceType==='legacyAc' || ['smartAc','systemAc'].includes(model.devices[arrayIndices[0]].deviceType));"
          }
        },
        {
          "key": "devices[].timeoutSec",
          "type": "number",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && model.devices[arrayIndices[0]].deviceType==='legacyAc';"
          }
        },
        {
          "key": "devices[].cacheDurationSec",
          "type": "number",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && model.devices[arrayIndices[0]].deviceType==='legacyAc';"
          }
        },
        {
          "key": "devices[].stateDumpFile",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && model.devices[arrayIndices[0]].deviceType==='legacyAc';"
          }
        },
        {
          "key": "devices[].manufacturer",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && model.devices[arrayIndices[0]].deviceType==='legacyAc';"
          }
        },
        {
          "key": "devices[].debug",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && model.devices[arrayIndices[0]].deviceType==='legacyAc';"
          }
        },
        {
          "key": "devices[].transport",
          "type": "select",
          "titleMap": [
            {
              "value": "cloud",
              "name": "클라우드"
            },
            {
              "value": "local",
              "name": "로컬"
            }
          ],
          "condition": {
            "functionBody": "const d = model.devices[arrayIndices[0]]; return !!d && ['smartAc','systemAc','washer','dryer'].includes(d.deviceType);"
          }
        },
        {
          "key": "devices[].local.host",
          "condition": {
            "functionBody": "const d = model.devices[arrayIndices[0]]; return !!d && ['smartAc','systemAc','washer','dryer','waterPurifier'].includes(d.deviceType) && (d.deviceType === 'waterPurifier' || d.transport === 'local');"
          }
        },
        {
          "key": "devices[].local.token",
          "condition": {
            "functionBody": "const d = model.devices[arrayIndices[0]]; return !!d && d.deviceType === 'washer' && d.transport === 'local';"
          }
        },
        {
          "key": "devices[].local.fallbackToCloud",
          "condition": {
            "functionBody": "const d = model.devices[arrayIndices[0]]; return !!d && ['smartAc','systemAc','washer','dryer'].includes(d.deviceType) && d.transport === 'local';"
          }
        },
        {
          "key": "devices[].model",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && ['legacyAc','smartAc','systemAc','washer','dryer'].includes(model.devices[arrayIndices[0]].deviceType);"
          }
        },
        {
          "key": "devices[].serialNumber",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && ['legacyAc','smartAc','systemAc','washer','dryer'].includes(model.devices[arrayIndices[0]].deviceType);"
          }
        },
        {
          "key": "devices[].hkCoolMode",
          "type": "select",
          "required": true,
          "titleMap": [
            {
              "value": "Cool",
              "name": "냉방"
            },
            {
              "value": "CoolClean",
              "name": "냉방청정"
            },
            {
              "value": "Dry",
              "name": "제습"
            },
            {
              "value": "DryClean",
              "name": "제습청정"
            },
            {
              "value": "Wind",
              "name": "송풍"
            },
            {
              "value": "Auto",
              "name": "자동"
            }
          ],
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && model.devices[arrayIndices[0]].deviceType==='legacyAc';"
          }
        },
        {
          "key": "devices[].legacySwingBinding",
          "type": "select",
          "required": true,
          "titleMap": [
            {
              "value": "comfort",
              "name": "무풍"
            },
            {
              "value": "wind",
              "name": "회전"
            },
            {
              "value": "none",
              "name": "사용 안 함"
            }
          ],
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && model.devices[arrayIndices[0]].deviceType==='legacyAc';"
          }
        },
        {
          "key": "devices[].legacyLockBinding",
          "type": "select",
          "required": true,
          "titleMap": [
            {
              "value": "autoClean",
              "name": "자동건조"
            },
            {
              "value": "none",
              "name": "사용 안 함"
            }
          ],
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && model.devices[arrayIndices[0]].deviceType==='legacyAc';"
          }
        },
        {
          "key": "devices[].legacyOnGuardSec",
          "type": "number",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && model.devices[arrayIndices[0]].deviceType==='legacyAc';"
          }
        },
        {
          "key": "devices[].legacyOnGuardStrategy",
          "type": "select",
          "titleMap": [
            {
              "value": "drop",
              "name": "무시"
            },
            {
              "value": "queue",
              "name": "끝난 뒤 전송"
            }
          ],
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && model.devices[arrayIndices[0]].deviceType==='legacyAc';"
          }
        },
        {
          "key": "devices[].coolModeCommand",
          "type": "select",
          "required": true,
          "titleMap": [
            {
              "value": "cool",
              "name": "냉방"
            },
            {
              "value": "dry",
              "name": "제습"
            },
            {
              "value": "fan",
              "name": "송풍"
            },
            {
              "value": "aIComfort",
              "name": "AI 쾌적"
            },
            {
              "value": "auto",
              "name": "자동"
            }
          ],
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && ['smartAc','systemAc'].includes(model.devices[arrayIndices[0]].deviceType);"
          }
        },
        {
          "key": "devices[].swingBinding",
          "type": "select",
          "required": true,
          "titleMap": [
            {
              "value": "windFree",
              "name": "무풍"
            },
            {
              "value": "rotate",
              "name": "회전"
            },
            {
              "value": "none",
              "name": "사용 안 함"
            }
          ],
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && model.devices[arrayIndices[0]].deviceType===\"smartAc\";"
          }
        },
        {
          "key": "devices[].systemSwingBinding",
          "type": "select",
          "required": true,
          "titleMap": [
            {
              "value": "windFree",
              "name": "무풍"
            },
            {
              "value": "All",
              "name": "상하좌우"
            },
            {
              "value": "Up_And_Low",
              "name": "상하 바람"
            },
            {
              "value": "Left_And_Right",
              "name": "좌우 바람"
            },
            {
              "value": "none",
              "name": "사용 안 함"
            }
          ],
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && model.devices[arrayIndices[0]].deviceType===\"systemAc\";"
          }
        },
        {
          "key": "devices[].lockBinding",
          "type": "select",
          "required": true,
          "titleMap": [
            {
              "value": "autoClean",
              "name": "자동건조"
            },
            {
              "value": "none",
              "name": "사용 안 함"
            }
          ],
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && ['smartAc','systemAc'].includes(model.devices[arrayIndices[0]].deviceType);"
          }
        },
        {
          "key": "devices[].resendModeOnPowerOn",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && (model.devices[arrayIndices[0]].deviceType==='legacyAc' || ['smartAc','systemAc'].includes(model.devices[arrayIndices[0]].deviceType));"
          }
        },
        {
          "key": "devices[].resendAutoCleanOnPowerOn",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && (model.devices[arrayIndices[0]].deviceType==='legacyAc' || ['smartAc','systemAc'].includes(model.devices[arrayIndices[0]].deviceType));"
          }
        },
        {
          "key": "devices[].resendSwingOffOnPowerOn",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && model.devices[arrayIndices[0]].deviceType==='legacyAc';"
          }
        },
        {
          "key": "devices[].powerOnResendStepSec",
          "type": "number",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && (model.devices[arrayIndices[0]].deviceType==='legacyAc' || ['smartAc','systemAc'].includes(model.devices[arrayIndices[0]].deviceType));"
          }
        },
        {
          "key": "devices[].hideSwingToggle",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && model.devices[arrayIndices[0]].deviceType==='legacyAc';"
          }
        },
        {
          "key": "devices[].hideLockToggle",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && model.devices[arrayIndices[0]].deviceType==='legacyAc';"
          }
        },
        {
          "key": "devices[].exposeWindFreeSwitch",
          "type": "checkbox",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && ['smartAc','systemAc'].includes(model.devices[arrayIndices[0]].deviceType);"
          }
        },
        {
          "key": "devices[].exposeAutoCleanSwitch",
          "type": "checkbox",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && ['smartAc','systemAc'].includes(model.devices[arrayIndices[0]].deviceType);"
          }
        },
        {
          "key": "devices[].enableNotificationSensor",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && ['washer','dryer'].includes(model.devices[arrayIndices[0]].deviceType);"
          }
        },
        {
          "key": "devices[].sensorName",
          "condition": {
            "functionBody": "const d = model.devices[arrayIndices[0]]; return !!d && ['washer','dryer'].includes(d.deviceType) && !!d.enableNotificationSensor && !(d.deviceType === 'washer' && d.splitCompartments === true);"
          }
        },
        {
          "key": "devices[].sensorPollInterval",
          "condition": {
            "functionBody": "const d = model.devices[arrayIndices[0]]; return !!d && ['washer','dryer','waterPurifier'].includes(d.deviceType);"
          }
        },
        {
          "key": "devices[].mqttSlug",
          "condition": {
            "functionBody": "const d = model.devices[arrayIndices[0]]; return !!d && ['smartAc','systemAc','washer','dryer','waterPurifier'].includes(d.deviceType);"
          }
        },
        {
          "key": "devices[].mqttExpose",
          "condition": {
            "functionBody": "const d = model.devices[arrayIndices[0]]; return !!d && ['smartAc','systemAc','washer','dryer','waterPurifier'].includes(d.deviceType);"
          }
        },
        {
          "key": "devices[].splitCompartments",
          "condition": {
            "functionBody": "const d = model.devices[arrayIndices[0]]; return !!d && d.deviceType === 'washer';"
          }
        },
        {
          "key": "devices[].mainCompartmentName",
          "condition": {
            "functionBody": "const d = model.devices[arrayIndices[0]]; return !!d && d.deviceType === 'washer' && d.splitCompartments === true;"
          }
        },
        {
          "key": "devices[].subCompartmentName",
          "condition": {
            "functionBody": "const d = model.devices[arrayIndices[0]]; return !!d && d.deviceType === 'washer' && d.splitCompartments === true;"
          }
        },
        {
          "key": "devices[].minTemp",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && model.devices[arrayIndices[0]].deviceType==='legacyAc';"
          }
        },
        {
          "key": "devices[].maxTemp",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && model.devices[arrayIndices[0]].deviceType==='legacyAc';"
          }
        },
        {
          "key": "devices[].temperatureMin",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && ['smartAc','systemAc'].includes(model.devices[arrayIndices[0]].deviceType);"
          }
        },
        {
          "key": "devices[].temperatureMax",
          "condition": {
            "functionBody": "return model.devices[arrayIndices[0]] && ['smartAc','systemAc'].includes(model.devices[arrayIndices[0]].deviceType);"
          }
        }
      ]
    }
  ]
}