Skill: jftech/jf-open-pro-smart-doorlock-control (version 1.0.0)

--- name: jf-open-pro-smart-doorlock-control description: 杰峰智能门锁设备控制技能（开发版）。支持查询设备是否支持门锁能力、登录设备、获取设备接口访问令牌，以及远程一键开锁。 metadata: version: 1.0.0 author: JFTech category: device tags: - 杰峰 - 智能门锁 - 远程开锁 - 设备登录 - 设备能力 triggers: - 查询设备是否支持开锁 - 设备支持门锁吗 - 远程开锁 - 一键开锁 - 登录设备 - 获取设备Token prerequisites: - 配置必需的环境变量 - 设备需已完成配网和绑定 - 设备需在线 region: - CN: api-cn.jftechws.com (中国大陆) - AS: api-as.jftechws.com (亚洲) - EU: api-eu.jftechws.com (欧洲) - NA: api-na.jftechws.com (北美洲) --- # jf-open-pro-smart-doorlock-control - 杰峰智能门锁设备控制技能（开发版） ## 技能描述 支持杰峰智能门锁设备的远程控制功能： - **查询设备是否支持开锁** - 查询设备能力集，确认是否支持门锁功能 - **登录设备** - 对门锁设备进行登录认证 - **获取设备接口访问令牌** - 获取 deviceToken 用于后续操作 - **远程一键开锁** - 远程发送开锁指令，控制门锁开启 **适用场景：** - 远程给家人或访客开锁 - 确认设备是否支持门锁功能 - 设备登录与令牌管理 ## 触发词 - 查询设备是否支持开锁 / 设备支持门锁吗 - 查询门锁配置 / 获取门锁配置 - 远程开锁 / 一键开锁 / 开锁 - 登录设备 / 设备登录 - 获取设备 Token ## 前置条件 ### 硬件要求 1. **智能门锁** - 设备需为杰峰智能门锁设备 ### 必需配置 1. **签名算法** - 使用杰峰官方移位加密算法生成 signature 2. **时间戳算法** - counter(7 位) + timeMillis(13 位)，实时生成 3. **设备绑定** - 设备需先绑定到开放平台账号 ### 环境变量 | 变量名 | 说明 | 默认值 | 必需 | |--------|------|--------|------| | `JF_UUID` | 开放平台用户 uuid | - | ✅ | | `JF_APP_KEY` | 开放平台应用 appKey | - | ✅ | | `JF_APP_SECRET` | 开放平台应用密钥 | - | ✅ | | `JF_MOVE_CARD` | 移动卡标识（用于签名） | `2` | ✅ | | `JF_DEVICE_SN` | 设备序列号 | - | ✅ | | `JF_DEVICE_TOKEN` | 设备接口访问令牌 | - | ✅ | | `JF_ENDPOINT` | API 接入地址 | `api-cn.jftechws.com` | ❌ | ## API 接口 | 功能 | 地址 | 方法 | 需要 Token | 需要在线 | |------|------|------|------------|----------| | 查询设备能力 | `POST /gwp/v3/rtc/device/getability/{token}` | POST | ✅ | ✅ | | 获取门锁配置 | `POST /gwp/v3/rtc/device/doorLockTransparent/{token}` | POST | ✅ | ✅ | | 登录设备 | `POST /gwp/v3/rtc/device/login/{token}` | POST | ✅ | ✅ | | 获取设备 Token | `POST /gwp/v3/rtc/device/token` | POST | ❌ | ❌ | | 远程开锁 | `POST /gwp/v3/rtc/device/doorLockRemoteUnlock/{token}` | POST | ✅ | ✅ | ## 核心功能 ### 1. 查询设备是否支持开锁 **API:** `POST /gwp/v3/rtc/device/getability/{deviceToken}` **Name:** `DoorFunction` **说明：** 查询设备能力集，确认设备是否支持门锁功能及相关特性。 **响应参数：** | 字段 | 类型 | 说明 | |------|------|------| | `DoorFunction.DoorAbilityMask` | int | 门锁能力掩码（按位标识各类门锁能力） | | `DoorFunction.LocalAbilityMask` | int | 本地能力掩码（按位标识本地支持的能力） | | `DoorFunction.UserManage` | object | 用户管理能力信息 | | `DoorFunction.UserManage.TypeMsk` | int | 用户类型掩码 | | `DoorFunction.AdminAbility` | int | 管理员能力值 | | `DoorFunction.TmpPwdRange` | object | 临时密码范围配置 | | `DoorFunction.TmpPwdRange.Start` | int | 临时密码起始值 | | `DoorFunction.TmpPwdRange.End` | int | 临时密码结束值 | | `DoorFunction.TmpPwdRange.UsrTypeMask` | int | 临时密码适用的用户类型掩码 | | `DoorFunction.TipVolume` | int | 提示音量档位 | | `DoorFunction.RebootMachine` | int | 重启设备支持标识 | | `DoorFunction.OPTrans` | int | 透明传输能力标识 | | `DoorFunction.IOTFaceModuleOta` | object | 人脸识别模块 OTA 信息 | | `DoorFunction.IOTFaceModuleOta.Enable` | bool | 人脸识别模块 OTA 使能状态 | | `DoorFunction.IOTFaceModuleOta.Pid` | int | 人脸识别模块产品 ID | | `DoorFunction.IOTFaceModuleOta.ModuleName` | string | 人脸识别模块名称 | | `DoorFunction.IOTFaceModuleOta.NavVersion` | string | 人脸识别模块导航版本 | | `DoorFunction.QsCapMask` | int | 快速启动能力掩码 | | `DoorFunction.QsPid` | int | 快速启动产品 ID | | `DoorFunction.NavVersion` | string | 导航版本号 | ### 2. 获取门锁配置 **API:** `POST /gwp/v3/rtc/device/doorLockTransparent/{deviceToken}` **请求参数：** | 参数 | 类型 | 必需 | 说明 | 取值 | |------|------|------|------|------| | `Name` | string | ✅ | 方法名称 | `OPDoorLockProCmd` | | `OPDoorLockProCmd.Cmd` | string | ✅ | 命令码 | `GetDoorConfig` | **请求示例：** ```json { "Name": "OPDoorLockProCmd", "OPDoorLockProCmd": { "Cmd": "GetDoorConfig" } } ``` **响应参数：** | 字段 | 类型 | 说明 | |------|------|------| | `DoorConfig.PirConfig.Enable` | bool | 逗留侦测是否使能 | | `DoorConfig.PirConfig.sensitivity` | int | 灵敏度：低/中/高（0/1/2） | | `DoorConfig.UnlockDirection.Direction` | int | 方向：左/右（0/1） | | `DoorConfig.AutoLock.LockMode` | int | 手动上锁/自动上锁（0/1） | | `DoorConfig.AutoLock.AutoLockInterval` | int | 自动锁定时间：10~99 秒 | | `DoorConfig.UnlockMode.mode` | int | 单开模式/组合模式（0/1） | | `DoorConfig.VolumeControl.Volume` | int | 音量：静音/小/中/大（0/1/2/3） | | `DoorConfig.FaceAlarmTone.Enable` | bool | 人脸声音开关是否使能 | | `DoorConfig.MotorTorque.Torque` | int | 扭矩：小/中/大（0/1/2） | | `DoorConfig.NormalOpenMode.Mode` | int | 常开模式：关闭/开启（0/1） | | `DoorConfig.DeployDefense.Enable` | int | 布防状态：撤防/布防（0/1） | ### 3. 登录设备 **API:** `POST /gwp/v3/rtc/device/login/{deviceToken}` **请求参数：** | 参数 | 类型 | 必需 | 说明 | 示例 | |------|------|------|------|------| | `UserName` | string | ✅ | 设备用户名，恢复出厂后部分设备默认为 `admin` | `admin` | | `PassWord` | string | ❌ | 设备登录密码 | `123456` | | `KeepaliveTime` | int | ❌ | 保活时长，单位秒（默认 5 分钟，最长 24 小时） | `300` | **响应参数：** | 字段 | 类型 | 说明 | |------|------|------| | `Ret` | int | 设备登录状态码（100=成功） | | `DeviceType` | string | 设备类型（IOT/IPC/DVR 等） | | `AliveInterval` | int | 客户端与设备保活时间周期，单位秒 | | `ChannelNum` | int | 设备通道数 | | `SessionID` | string | 会话 ID | **说明：** 设备默认 1 分钟无操作自动断开连接，若需保持长时间连接可设置 `KeepaliveTime` 进行保活。 ### 3. 获取设备接口访问令牌 **API:** `POST /gwp/v3/rtc/device/token` **说明：** 获取 deviceToken，用于后续的门锁操作。Token 有效期 24 小时。 ### 4. 远程一键开锁 **API:** `POST /gwp/v3/rtc/device/doorLockRemoteUnlock/{deviceToken}` **请求参数：** | 参数 | 类型 | 必需 | 说明 | 取值 | |------|------|------|------|------| | `sn` | string | ✅ | 设备序列号 | - | | `props.doorLock.remoteUnlock.password` | string | ❌ | 锁端密码（密码开锁时必填） | - | | `props.doorLock.remoteOneKeyUnlock` | int | ❌ | 无密码一键开锁标记，填 `1` | - | | `props.doorLock.userType` | int | ❌ | 远程开锁为 `5` | `5` | | `props.doorLock.memberID` | int | ❌ | 成员 ID，`1` 表示主账号 | `1` | **说明：** 支持两种开锁方式。提供密码时使用密码开锁，不提供密码时使用无密码一键开锁。 ## 使用示例 ### 环境准备 ```bash # 设置环境变量（使用占位符，请替换为实际值） export JF_UUID="uuidxxxx" export JF_APP_KEY="appkeyxxxx" export JF_APP_SECRET="appsecretxxxx" export JF_MOVE_CARD="2" export JF_DEVICE_SN="snxxx1" export JF_DEVICE_TOKEN="NTQ0NzQ3YmE3MXwyYzFk..." export JF_ENDPOINT="api-cn.jftechws.com" ``` ### 1. 查询设备是否支持开锁 ```bash cd ~/.qoderwork/skills/jf-open-pro-smart-doorlock-control/scripts python3 doorlock_control.py --action check-support ``` ### 2. 获取门锁配置 ```bash python3 doorlock_control.py --action get-config ``` ### 3. 登录设备 ```bash # 使用默认用户名 admin 登录 python3 doorlock_control.py --action login --username "admin" --password "123456" # 设置保活时长为 10 分钟 python3 doorlock_control.py --action login --username "admin" --password "123456" --keepalive-time 600 ``` ### 4. 获取设备 Token ```bash python3 doorlock_control.py --action get-token --device-sn "snxxx1" ``` ### 5. 远程一键开锁 ```bash # 无密码一键开锁 python3 doorlock_control.py --action unlock # 使用密码开锁 python3 doorlock_control.py --action unlock --password "123456" ``` ## 状态码 ### 平台状态码 | code | 说明 | 处理建议 | |------|------|----------| | 2000 | 成功 | - | | 28007 | Header 参数错误 | 检查 uuid、appKey、timeMillis、signature | | 40103 | 无效 Token | deviceToken 过期，重新获取 | | 50000 | 服务器内部错误 | 联系杰峰技术支持 | ### 设备状态码（Ret） | Ret | 说明 | |-----|------| | 100 | 成功 | | 103 | 非法请求（设备不支持该功能） | | 106 | 用户名或密码错误 | ## 注意事项 1. **设备要求** - 仅智能门锁设备支持此功能 2. **deviceToken 有效期** - 24 小时，过期需重新获取 3. **设备在线要求** - 远程开锁需要设备在线 4. **安全性** - 远程开锁涉及物理安全，请谨慎操作 5. **登录要求** - 部分设备可能需要先登录才能执行开锁操作 ## 相关文件 | 文件 | 说明 | |------|------| | `SKILL.md` | 技能文档 | | `scripts/doorlock_control.py` | Python 执行脚本 | | `scripts/crypto.py` | 签名/时间戳加密工具（复用） | ## 参考文档 - [杰峰开放平台](https://docs.jftech.com)

path: scripts/crypto.py
content: #!/usr/bin/env python3
"""
杰峰 OpenAPI 加密工具

包含：
- 签名算法
- 时间戳生成
"""

import hashlib
import time


def get_counter() -> str:
    """
    获取计数器（7 位，不足补 0）
    
    Returns:
        7 位计数字符串
    """
    counter = 1
    if counter < 10:
        return '000000' + str(counter)
    elif counter < 100:
        return '00000' + str(counter)
    elif counter < 1000:
        return '0000' + str(counter)
    elif counter < 10000:
        return '000' + str(counter)
    elif counter < 100000:
        return '00' + str(counter)
    elif counter < 1000000:
        return '0' + str(counter)
    else:
        return str(counter)


def get_time_millis() -> str:
    """
    获取组合时间戳（counter 7 位 + timeMillis 13 位）
    
    Returns:
        20 位时间戳字符串
    """
    counter = get_counter()
    time_millis = str(int(time.time() * 1000))
    return counter + time_millis


def str_to_bytes(s: str) -> list:
    """
    字符串转字节数组（处理 UTF-8 多字节字符）
    
    Args:
        s: 输入字符串
        
    Returns:
        字节数组
    """
    bytes_arr = []
    for char in s:
        code = ord(char)
        if code >= 0x010000 and code <= 0x10FFFF:
            bytes_arr.append(((code >> 18) & 0x07) | 0xF0)
            bytes_arr.append(((code >> 12) & 0x3F) | 0x80)
            bytes_arr.append(((code >> 6) & 0x3F) | 0x80)
            bytes_arr.append((code & 0x3F) | 0x80)
        elif code >= 0x000800 and code <= 0x00FFFF:
            bytes_arr.append(((code >> 12) & 0x0F) | 0xE0)
            bytes_arr.append(((code >> 6) & 0x3F) | 0x80)
            bytes_arr.append((code & 0x3F) | 0x80)
        elif code >= 0x000080 and code <= 0x0007FF:
            bytes_arr.append(((code >> 6) & 0x1F) | 0xC0)
            bytes_arr.append((code & 0x3F) | 0x80)
        else:
            bytes_arr.append(code & 0xFF)
    return bytes_arr


def change(encrypt_str: str, move_card: int) -> list:
    """
    简单移位算法（严格按照 Java 版本实现）
    
    Args:
        encrypt_str: 输入字符串
        move_card: 移动卡标识
        
    Returns:
        移位后的字节数组
    """
    try:
        encrypt_byte = list(encrypt_str.encode('iso-8859-1'))
    except UnicodeEncodeError:
        encrypt_byte = str_to_bytes(encrypt_str)
    
    encrypt_length = len(encrypt_byte)
    
    for i in range(encrypt_length):
        if (i % move_card) > ((encrypt_length - i) % move_card):
            temp = encrypt_byte[i]
        else:
            temp = encrypt_byte[encrypt_length - (i + 1)]
        
        encrypt_byte[i] = encrypt_byte[encrypt_length - (i + 1)]
        encrypt_byte[encrypt_length - (i + 1)] = temp
    
    return encrypt_byte


def merge_byte(encrypt_byte: list, change_byte: list) -> list:
    """
    合并字节数组
    
    Args:
        encrypt_byte: 原字节数组
        change_byte: 移位后的字节数组
        
    Returns:
        合并后的字节数组
    """
    encrypt_length = len(encrypt_byte)
    encrypt_length2 = encrypt_length * 2
    temp = [0] * encrypt_length2
    
    for i in range(encrypt_length):
        temp[i] = encrypt_byte[i]
        temp[encrypt_length2 - 1 - i] = change_byte[i]
    
    return temp


def generate_signature(uuid: str, app_key: str, app_secret: str, 
                       time_millis: str, move_card: int) -> str:
    """
    生成杰峰 OpenAPI 签名
    
    算法步骤：
    1. 将 uuid, appKey, appSecret, timeMillis 依次拼接
    2. 获取拼接后字符串的二进制数组
    3. 二进制数组进行简单移位算法
    4. 原二进制数组与移位后的二进制数组合并
    5. 将合并的二进制数组 md5 加密一次生成密钥
    
    Args:
        uuid: 客户唯一标识
        app_key: 应用 key
        app_secret: 应用密钥
        time_millis: 时间戳
        move_card: 移动取模基数
        
    Returns:
        32 位十六进制签名字符串
    """
    encrypt_str = uuid + app_key + app_secret + time_millis
    
    try:
        encrypt_byte = list(encrypt_str.encode('iso-8859-1'))
    except UnicodeEncodeError:
        encrypt_byte = str_to_bytes(encrypt_str)
    
    change_byte = change(encrypt_str, move_card)
    merge = merge_byte(encrypt_byte, change_byte)
    signature = hashlib.md5(bytes(merge)).hexdigest()
    
    return signature


# 测试
if __name__ == "__main__":
    uuid = "uuidxxxx"
    app_key = "appkeyxxxx"
    app_secret = "90f8bc17be2a425db6068c749deexxxx"
    time_millis = "00000011645153792342"
    move_card = 2
    
    signature = generate_signature(uuid, app_key, app_secret, time_millis, move_card)
    
    print(f"Signature: {signature}")
    print(f"注意：签名算法需与杰峰服务器验证逻辑一致")


path: scripts/doorlock_control.py
content: #!/usr/bin/env python3
"""
杰峰智能门锁设备控制技能（开发版）

支持功能：
- 查询设备是否支持开锁
- 登录设备
- 获取设备接口访问令牌
- 远程一键开锁
"""

import os
import sys
import json
import argparse
import requests
from typing import Optional, Dict, Any, List

# 导入加密工具（复用）
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
try:
    from crypto import get_time_millis, generate_signature
except ImportError:
    print("[错误] 错误：找不到 crypto.py 模块")
    print("   请确保 scripts/crypto.py 存在")
    sys.exit(1)

# API 基础地址（可通过环境变量切换）
JF_ENDPOINT = os.getenv("JF_ENDPOINT", "api-cn.jftechws.com")
JF_BASE_URL = f"https://{JF_ENDPOINT}/gwp/v3"


def get_headers(uuid: str, app_key: str, app_secret: str, move_card: int) -> Dict[str, str]:
    """生成请求头（包含签名和时间戳）"""
    time_millis = get_time_millis()
    signature = generate_signature(uuid, app_key, app_secret, time_millis, move_card)

    return {
        "Content-Type": "application/json; charset=UTF-8",
        "uuid": uuid,
        "appKey": app_key,
        "timeMillis": time_millis,
        "signature": signature,
        "X-Request-Id": os.urandom(16).hex()
    }


def get_device_token(uuid: str, app_key: str, app_secret: str,
                     move_card: int, sns: List[str],
                     endpoint: str = "api-cn.jftechws.com",
                     access_token: str = "") -> List[Dict[str, str]]:
    """
    获取设备接口访问令牌（通用函数）

    API: POST /gwp/v3/rtc/device/token
    """
    base_url = f"https://{endpoint}/gwp/v3"
    url = f"{base_url}/rtc/device/token"
    headers = get_headers(uuid, app_key, app_secret, move_card)

    body = {"sns": sns}
    if access_token:
        body["accessToken"] = access_token

    response = requests.post(url, headers=headers, json=body, timeout=30)
    result = response.json()

    if result.get("code") != 2000:
        raise RuntimeError(f"获取设备 Token 失败：{result.get('msg', '未知错误')}")

    data = result.get("data", [])
    if not data:
        raise RuntimeError("设备不属于当前用户，或设备未绑定")

    return data


def check_doorlock_support(device_token: str, uuid: str, app_key: str,
                            app_secret: str, move_card: int) -> Dict[str, Any]:
    """
    查询设备是否支持门锁功能

    API: POST /gwp/v3/rtc/device/getability/{deviceToken}
    Name: DoorFunction
    """
    url = f"{JF_BASE_URL}/rtc/device/getability/{device_token}"
    headers = get_headers(uuid, app_key, app_secret, move_card)

    body = {"Name": "DoorFunction"}

    response = requests.post(url, headers=headers, json=body, timeout=30)
    result = response.json()

    if result.get("code") != 2000:
        raise RuntimeError(f"查询设备能力失败：{result.get('msg', '未知错误')}")

    return result.get("data", {})


def get_door_config(device_token: str, uuid: str, app_key: str,
                     app_secret: str, move_card: int) -> Dict[str, Any]:
    """
    获取门锁配置

    API: POST /gwp/v3/rtc/device/doorLockTransparent/{deviceToken}
    """
    url = f"{JF_BASE_URL}/rtc/device/doorLockTransparent/{device_token}"
    headers = get_headers(uuid, app_key, app_secret, move_card)

    body = {
        "Name": "OPDoorLockProCmd",
        "OPDoorLockProCmd": {
            "Cmd": "GetDoorConfig"
        }
    }

    response = requests.post(url, headers=headers, json=body, timeout=30)
    result = response.json()

    if result.get("code") != 2000:
        raise RuntimeError(f"获取门锁配置失败：{result.get('msg', '未知错误')}")

    return result.get("data", {})


def login_device(device_token: str, uuid: str, app_key: str,
                 app_secret: str, move_card: int,
                 device_sn: str, username: str, password: str = "",
                 keepalive_time: int = 0) -> Dict[str, Any]:
    """
    登录设备

    API: POST /gwp/v3/rtc/device/login/{deviceToken}
    """
    url = f"{JF_BASE_URL}/rtc/device/login/{device_token}"
    headers = get_headers(uuid, app_key, app_secret, move_card)

    body = {"UserName": username}
    if password:
        body["PassWord"] = password
    if keepalive_time > 0:
        body["KeepaliveTime"] = keepalive_time

    response = requests.post(url, headers=headers, json=body, timeout=30)
    result = response.json()

    if result.get("code") != 2000:
        raise RuntimeError(f"设备登录失败：{result.get('msg', '未知错误')}")

    if result.get("data", {}).get("Ret") != 100:
        raise RuntimeError(f"设备返回错误：Ret={result.get('data', {}).get('Ret')}")

    return result.get("data", {})


def unlock_door(device_token: str, uuid: str, app_key: str,
                app_secret: str, move_card: int,
                device_sn: str, password: str = "") -> Dict[str, Any]:
    """
    远程一键开锁

    API: POST /gwp/v3/rtc/device/doorLockRemoteUnlock/{deviceToken}
    """
    url = f"{JF_BASE_URL}/rtc/device/doorLockRemoteUnlock/{device_token}"
    headers = get_headers(uuid, app_key, app_secret, move_card)

    body = {"sn": device_sn, "props": {}}

    if password:
        body["props"]["doorLock"] = {
            "remoteUnlock": {
                "password": password
            }
        }
    else:
        body["props"]["doorLock"] = {
            "remoteOneKeyUnlock": 1,
            "userType": 5,
            "memberID": 1
        }

    response = requests.post(url, headers=headers, json=body, timeout=30)
    result = response.json()

    if result.get("code") != 2000:
        raise RuntimeError(f"远程开锁失败：{result.get('msg', '未知错误')}")

    if result.get("data", {}).get("Ret") != 100:
        ret_msg = result.get("data", {}).get("retMsg", "")
        raise RuntimeError(f"设备返回错误：Ret={result.get('data', {}).get('Ret')}{f' ({ret_msg})' if ret_msg else ''}")

    return result.get("data", {})


# ============== 动作处理函数 ==============

def get_token_action(args: argparse.Namespace) -> int:
    """执行获取设备 Token 操作"""
    try:
        sns = args.device_sn.split(",") if args.device_sn else []
        if not sns:
            print("[错误] 错误：缺少 --device-sn 或 JF_DEVICE_SN 环境变量", file=sys.stderr)
            return 1

        tokens = get_device_token(
            uuid=args.uuid,
            app_key=args.app_key,
            app_secret=args.app_secret,
            move_card=args.move_card,
            sns=sns,
            endpoint=JF_ENDPOINT
        )

        print("[OK] 获取成功")
        for item in tokens:
            print(f"   设备 SN: {item.get('sn')}")
            print(f"   DeviceToken: {item.get('token')}")

        return 0

    except RuntimeError as e:
        print(f"[错误] 错误：{e}", file=sys.stderr)
        return 1


def check_support_action(args: argparse.Namespace) -> int:
    """执行查询设备是否支持开锁操作"""
    try:
        result = check_doorlock_support(
            device_token=args.device_token,
            uuid=args.uuid,
            app_key=args.app_key,
            app_secret=args.app_secret,
            move_card=args.move_card
        )

        door_func = result.get("DoorFunction", {})
        if not door_func and "DoorAbilityMask" in result:
            door_func = result

        if door_func:
            for key, value in door_func.items():
                print(f"   {key}: {value}")
        else:
            print("[警告] 设备未返回 DoorFunction 能力信息")

        return 0

    except RuntimeError as e:
        print(f"[错误] {e}", file=sys.stderr)
        return 1


def get_config_action(args: argparse.Namespace) -> int:
    """执行获取门锁配置操作"""
    try:
        result = get_door_config(
            device_token=args.device_token,
            uuid=args.uuid,
            app_key=args.app_key,
            app_secret=args.app_secret,
            move_card=args.move_card
        )

        config = result.get("DoorConfig", {})
        if config:
            for key, value in config.items():
                print(f"   {key}: {value}")
        else:
            print("[警告] 设备未返回门锁配置信息")

        return 0

    except RuntimeError as e:
        print(f"[错误] {e}", file=sys.stderr)
        return 1


def login_action(args: argparse.Namespace) -> int:
    """执行登录设备操作"""
    try:
        result = login_device(
            device_token=args.device_token,
            uuid=args.uuid,
            app_key=args.app_key,
            app_secret=args.app_secret,
            move_card=args.move_card,
            device_sn=args.device_sn,
            username=args.username,
            [REDACTED_SECRET],
            keepalive_time=args.keepalive_time
        )

        print("[OK] 登录成功")
        for key, value in result.items():
            print(f"   {key}: {value}")
        return 0

    except RuntimeError as e:
        print(f"[错误] {e}", file=sys.stderr)
        return 1


def unlock_action(args: argparse.Namespace) -> int:
    """执行远程一键开锁操作"""
    try:
        result = unlock_door(
            device_token=args.device_token,
            uuid=args.uuid,
            app_key=args.app_key,
            app_secret=args.app_secret,
            move_card=args.move_card,
            device_sn=args.device_sn,
            [REDACTED_SECRET]
        )

        print("[OK] 开锁指令发送成功")
        for key, value in result.items():
            print(f"   {key}: {value}")
        return 0

    except RuntimeError as e:
        print(f"[错误] {e}", file=sys.stderr)
        return 1


def main():
    """主函数"""
    parser = argparse.ArgumentParser(description="杰峰智能门锁设备控制技能")

    # 全局参数
    parser.add_argument("--action", required=True,
                        choices=["get-token", "check-support", "get-config", "login", "unlock"],
                        help="操作类型")
    parser.add_argument("--uuid", default=os.getenv("JF_UUID"),
                        help="开放平台用户 uuid")
    parser.add_argument("--app-key", default=os.getenv("JF_APP_KEY"),
                        help="应用 appKey")
    parser.add_argument("--app-secret", default=os.getenv("JF_APP_SECRET"),
                        help="应用密钥")
    parser.add_argument("--move-card", type=int, default=os.getenv("JF_MOVE_CARD", "2"),
                        help="移动卡标识")
    parser.add_argument("--device-sn", default=os.getenv("JF_DEVICE_SN"),
                        help="设备序列号")
    parser.add_argument("--device-token", default=os.getenv("JF_DEVICE_TOKEN"),
                        help="设备接口访问令牌")
    parser.add_argument("--password", default="",
                        help="锁端密码/设备登录密码（根据操作类型使用）")
    parser.add_argument("--username", default="admin",
                        help="设备登录用户名（默认 admin）")
    parser.add_argument("--keepalive-time", type=int, default=0,
                        help="设备登录保活时长，单位秒（默认 0 表示使用设备默认）")

    args = parser.parse_args()

    # 验证必需参数
    if not args.uuid:
        print("[错误] 错误：缺少 --uuid 或 JF_UUID 环境变量", file=sys.stderr)
        return 1
    if not args.app_key:
        print("[错误] 错误：缺少 --app-key 或 JF_APP_KEY 环境变量", file=sys.stderr)
        return 1
    if not args.app_secret:
        print("[错误] 错误：缺少 --app-secret 或 JF_APP_SECRET 环境变量", file=sys.stderr)
        return 1
    if args.action != "get-token" and not args.device_token:
        print("[错误] 错误：缺少 --device-token 或 JF_DEVICE_TOKEN 环境变量", file=sys.stderr)
        return 1

    # 执行对应操作
    if args.action == "get-token":
        return get_token_action(args)
    elif args.action == "check-support":
        return check_support_action(args)
    elif args.action == "get-config":
        return get_config_action(args)
    elif args.action == "login":
        return login_action(args)
    elif args.action == "unlock":
        return unlock_action(args)
    else:
        print(f"[错误] 未知操作：{args.action}", file=sys.stderr)
        return 1


if __name__ == "__main__":
    sys.exit(main())


Security scan verdicts — ClawScan: suspicious | static analysis: suspicious | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)