文件预览

image_flip.py

查看 jf-open-pro-device-image-flip 技能包中的文件内容。

文件内容

scripts/image_flip.py

#!/usr/bin/env python3
"""
杰峰设备画面翻转技能(开发版)

支持功能:
- 画面左右翻转(镜像)
- 画面上下翻转(倒置)
- 翻转状态查询
"""

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

# 导入加密工具(复用)
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_camera_param(device_token: str, uuid: str, app_key: str,
                     app_secret: str, move_card: int,
                     channel: int = 0) -> Dict[str, Any]:
    """
    获取摄像头基本参数配置
    
    API: POST /gwp/v3/rtc/device/getconfig/{deviceToken}
    Name: Camera.Param
    """
    url = f"{JF_BASE_URL}/rtc/device/getconfig/{device_token}"
    headers = get_headers(uuid, app_key, app_secret, move_card)
    
    body = {"Name": "Camera.Param"}
    if channel is not None:
        body["Channel"] = str(channel)
    
    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 set_camera_param(device_token: str, uuid: str, app_key: str,
                     app_secret: str, move_card: int,
                     config: Dict[str, Any]) -> Dict[str, Any]:
    """
    设置摄像头基本参数配置
    
    API: POST /gwp/v3/rtc/device/setconfig/{deviceToken}
    Name: Camera.Param
    """
    url = f"{JF_BASE_URL}/rtc/device/setconfig/{device_token}"
    headers = get_headers(uuid, app_key, app_secret, move_card)
    
    body = {
        "Name": "Camera.Param",
        "Camera.Param": config
    }
    
    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"设备返回错误:{result.get('data', {}).get('Ret')}")
    
    return result.get("data", {})


def hex_to_bool(hex_str: str) -> bool:
    """将十六进制字符串转换为布尔值"""
    return hex_str == "0x00000001"


def bool_to_hex(value: bool) -> str:
    """将布尔值转换为十六进制字符串"""
    return "0x00000001" if value else "0x00000000"


def get_default_camera_param() -> Dict[str, Any]:
    """获取默认的摄像头参数配置"""
    return {
        "AeSensitivity": 0,
        "ApertureMode": "0x00000000",
        "BLCMode": "0x00000000",
        "DayNightColor": "0x00000000",
        "Day_nfLevel": 0,
        "Night_nfLevel": 0,
        "DncThr": 0,
        "ElecLevel": 0,
        "EsShutter": "0x00000000",
        "ExposureParam": {
            "LeastTime": "0x00000000",
            "Level": 0,
            "MostTime": "0x00000000"
        },
        "GainParam": {
            "AutoGain": 0,
            "Gain": 50
        },
        "IRCUTMode": 0,
        "InfraredSwap": 0,
        "IrcutSwap": 0,
        "PictureFlip": "0x00000000",
        "PictureMirror": "0x00000000",
        "RejectFlicker": "0x00000000",
        "WhiteBalance": "0x00000000"
    }


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

def get_flip_config_action(args: argparse.Namespace) -> int:
    """执行获取画面翻转配置操作"""
    try:
        print(f"正在获取设备 {args.device_sn} 的画面翻转配置...")
        
        config_data = get_camera_param(
            device_token=args.device_token,
            uuid=args.uuid,
            app_key=args.app_key,
            app_secret=args.app_secret,
            move_card=args.move_card,
            channel=args.channel
        )
        
        camera_param = config_data.get("Camera.Param", [])
        if camera_param:
            cp = camera_param[0]
            mirror = hex_to_bool(cp.get("PictureMirror", "0x00000000"))
            flip = hex_to_bool(cp.get("PictureFlip", "0x00000000"))
            
            print()
            print("✅ 获取配置成功")
            print()
            print("📋 画面翻转状态:")
            print(f"   左右翻转(镜像): {'开启' if mirror else '关闭'}")
            print(f"   上下翻转(倒置): {'开启' if flip else '关闭'}")
            print()
            
            # 显示效果说明
            if mirror and flip:
                print("   💡 效果:画面旋转 180 度")
            elif mirror:
                print("   💡 效果:画面左右镜像")
            elif flip:
                print("   💡 效果:画面上下倒置")
            else:
                print("   💡 效果:原始画面(无翻转)")
            
            print()
            print(f"   会话 ID: {config_data.get('SessionID')}")
        else:
            print("⚠️  未找到摄像头参数配置")
        
        return 0
        
    except RuntimeError as e:
        print(f"❌ 错误:{e}", file=sys.stderr)
        return 1


def set_mirror_action(args: argparse.Namespace) -> int:
    """执行设置左右翻转(镜像)操作"""
    try:
        enable = args.enable.lower() == 'true'
        print(f"正在{'开启' if enable else '关闭'}设备 {args.device_sn} 的左右翻转(镜像)...")
        
        config_data = get_camera_param(
            device_token=args.device_token,
            uuid=args.uuid,
            app_key=args.app_key,
            app_secret=args.app_secret,
            move_card=args.move_card,
            channel=args.channel
        )
        
        camera_param = config_data.get("Camera.Param", [])
        if not camera_param:
            camera_param = [get_default_camera_param()]
        
        camera_param[0]["PictureMirror"] = bool_to_hex(enable)
        
        result = set_camera_param(
            device_token=args.device_token,
            uuid=args.uuid,
            app_key=args.app_key,
            app_secret=args.app_secret,
            move_card=args.move_card,
            config=camera_param
        )
        
        print()
        print("✅ 设置成功")
        print(f"   左右翻转(镜像): {'开启' if enable else '关闭'}")
        print(f"   会话 ID: {result.get('SessionID')}")
        
        return 0
        
    except RuntimeError as e:
        print(f"❌ 错误:{e}", file=sys.stderr)
        return 1


def set_flip_action(args: argparse.Namespace) -> int:
    """执行设置上下翻转操作"""
    try:
        enable = args.enable.lower() == 'true'
        print(f"正在{'开启' if enable else '关闭'}设备 {args.device_sn} 的上下翻转...")
        
        config_data = get_camera_param(
            device_token=args.device_token,
            uuid=args.uuid,
            app_key=args.app_key,
            app_secret=args.app_secret,
            move_card=args.move_card,
            channel=args.channel
        )
        
        camera_param = config_data.get("Camera.Param", [])
        if not camera_param:
            camera_param = [get_default_camera_param()]
        
        camera_param[0]["PictureFlip"] = bool_to_hex(enable)
        
        result = set_camera_param(
            device_token=args.device_token,
            uuid=args.uuid,
            app_key=args.app_key,
            app_secret=args.app_secret,
            move_card=args.move_card,
            config=camera_param
        )
        
        print()
        print("✅ 设置成功")
        print(f"   上下翻转(倒置): {'开启' if enable else '关闭'}")
        print(f"   会话 ID: {result.get('SessionID')}")
        
        return 0
        
    except RuntimeError as e:
        print(f"❌ 错误:{e}", file=sys.stderr)
        return 1


def set_both_action(args: argparse.Namespace) -> int:
    """执行同时设置左右和上下翻转操作"""
    try:
        mirror = args.mirror.lower() == 'true'
        flip = args.flip.lower() == 'true'
        
        print(f"正在设置设备 {args.device_sn} 的画面翻转...")
        print(f"   左右翻转(镜像): {'开启' if mirror else '关闭'}")
        print(f"   上下翻转(倒置): {'开启' if flip else '关闭'}")
        
        config_data = get_camera_param(
            device_token=args.device_token,
            uuid=args.uuid,
            app_key=args.app_key,
            app_secret=args.app_secret,
            move_card=args.move_card,
            channel=args.channel
        )
        
        camera_param = config_data.get("Camera.Param", [])
        if not camera_param:
            camera_param = [get_default_camera_param()]
        
        camera_param[0]["PictureMirror"] = bool_to_hex(mirror)
        camera_param[0]["PictureFlip"] = bool_to_hex(flip)
        
        result = set_camera_param(
            device_token=args.device_token,
            uuid=args.uuid,
            app_key=args.app_key,
            app_secret=args.app_secret,
            move_card=args.move_card,
            config=camera_param
        )
        
        print()
        print("✅ 设置成功")
        
        if mirror and flip:
            print("   效果:画面旋转 180 度")
        elif mirror:
            print("   效果:画面左右镜像")
        elif flip:
            print("   效果:画面上下倒置")
        else:
            print("   效果:原始画面(无翻转)")
        
        print(f"   会话 ID: {result.get('SessionID')}")
        
        return 0
        
    except RuntimeError as e:
        print(f"❌ 错误:{e}", file=sys.stderr)
        return 1


def reset_action(args: argparse.Namespace) -> int:
    """执行重置画面方向操作"""
    try:
        print(f"正在重置设备 {args.device_sn} 的画面方向...")
        
        config_data = get_camera_param(
            device_token=args.device_token,
            uuid=args.uuid,
            app_key=args.app_key,
            app_secret=args.app_secret,
            move_card=args.move_card,
            channel=args.channel
        )
        
        camera_param = config_data.get("Camera.Param", [])
        if not camera_param:
            camera_param = [get_default_camera_param()]
        
        camera_param[0]["PictureMirror"] = "0x00000000"
        camera_param[0]["PictureFlip"] = "0x00000000"
        
        result = set_camera_param(
            device_token=args.device_token,
            uuid=args.uuid,
            app_key=args.app_key,
            app_secret=args.app_secret,
            move_card=args.move_card,
            config=camera_param
        )
        
        print()
        print("✅ 重置成功")
        print("   画面已恢复为原始方向(无翻转)")
        print(f"   会话 ID: {result.get('SessionID')}")
        
        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-flip-config", "set-mirror", "set-flip",
                            "set-both", "reset"
                        ],
                        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("--channel", type=int, default=0,
                        help="通道号(默认 0)")
    
    # set-mirror / set-flip 参数
    parser.add_argument("--enable", default="true",
                        help="是否开启(true/false)")
    
    # set-both 参数
    parser.add_argument("--mirror", default="true",
                        help="是否开启左右翻转(true/false)")
    parser.add_argument("--flip", default="true",
                        help="是否开启上下翻转(true/false)")
    
    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 not args.device_token:
        print("❌ 错误:缺少 --device-token 或 JF_DEVICE_TOKEN 环境变量", file=sys.stderr)
        return 1
    
    # 执行对应操作
    if args.action == "get-flip-config":
        return get_flip_config_action(args)
    elif args.action == "set-mirror":
        return set_mirror_action(args)
    elif args.action == "set-flip":
        return set_flip_action(args)
    elif args.action == "set-both":
        return set_both_action(args)
    elif args.action == "reset":
        return reset_action(args)
    else:
        print(f"❌ 未知操作:{args.action}", file=sys.stderr)
        return 1


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