Metadata-Version: 2.4
Name: py-hikvision
Version: 1.0.0
Summary: 一个用于与海康威视 iSecure Center (ISC) API 交互的 Python 客户端库
Author-email: Guolei <174000902@qq.com>
License: MIT License
        
        Copyright (c) 2026 郭磊
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Homepage, https://gitee.com/guolei19850528/py_hikvision
Project-URL: Repository, https://gitee.com/guolei19850528/py_hikvision.git
Keywords: isc,hikvision,python,client,api,海康威视,iSecure Center,security,surveillance
Classifier: License :: OSI Approved :: MIT License
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: Topic :: Security
Classifier: Topic :: System :: Networking
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Operating System :: OS Independent
Classifier: Environment :: Web Environment
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.27.0
Requires-Dist: pydantic>=2.0
Requires-Dist: jsonpath-ng>=1.5.3
Requires-Dist: jsonschema>=4.21.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
Requires-Dist: setuptools>=61.0; extra == "dev"
Requires-Dist: twine>=4.0; extra == "dev"
Requires-Dist: flake8>=6.0; extra == "dev"
Requires-Dist: black>=23.0; extra == "dev"
Requires-Dist: isort>=5.12.0; extra == "dev"
Dynamic: license-file

# py-hikvision

一个用于与海康威视 iSecure Center (ISC) API 交互的 Python 客户端库。

## 功能特性

- 🔐 **API 认证**: 支持 HMAC-SHA256 签名认证机制，符合海康威视官方 API 规范
- 🚀 **同步/异步请求**: 支持同步和异步两种 HTTP 请求方式
- 📋 **响应模型**: 使用 Pydantic 进行响应数据校验和解析
- 🔧 **工具函数**: 提供时间戳、UUID、JSONPath 查询等实用工具
- 📦 **类型安全**: 完整的类型提示，支持现代 IDE 智能提示
- 🧪 **测试支持**: 完善的测试配置，支持 pytest 和代码覆盖率

## 安装

```bash
pip install py_hikvision
```

或者使用 uv（推荐）:

```bash
uv add py_hikvision
```

## 快速开始

### 基本使用

```python
from py_hikvision.isc import Isc
from py_hikvision.isc.utils import convert_to_code_eq_0

# 初始化客户端
isc_inst = Isc(
    host="https://your-isc-server.com",
    ak="your-access-key",
    sk="your-secret-key"
)

# 发送同步请求
response = isc_inst.request(
    method="POST",
    url="/api/parking/info",
    json={"plateNo": "京A12345"}
)
print(convert_to_code_eq_0(response))

# 发送异步请求
async def fetch_data():
    response = await isc_inst.async_request(
        method="GET",
        url="/api/parking/list"
    )
    return convert_to_code_eq_0(response)
```

### 使用自定义客户端

```python
import httpx
from py_hikvision.isc import Isc
from py_hikvision.isc.utils import convert_to_code_eq_0

# 创建自定义客户端
custom_client = httpx.Client(
    base_url="https://your-isc-server.com",
    timeout=120,
    verify=False
)

isc_inst = Isc(
    host="https://your-isc-server.com",
    ak="your-access-key",
    sk="your-secret-key"
)

# 使用自定义客户端发送请求
response = isc_inst.request(
    method="GET",
    url="/api/parking/list"
)
print(convert_to_code_eq_0(response))
print(response.json())
```

### 工具函数示例

```python
from py_hikvision.isc.utils import (
    timestamp,
    nonce,
    json_find_first,
    url_add_artemis_prefix
)

# 生成时间戳（毫秒）
ts = timestamp()
print(f"当前时间戳: {ts}")

# 生成随机 UUID
random_nonce = nonce()
print(f"随机 UUID: {random_nonce}")

# JSONPath 查询
data = {
    "code": 0,
    "msg": "success",
    "data": {
        "list": [
            {"id": 1, "name": "设备1"},
            {"id": 2, "name": "设备2"}
        ]
    }
}
first_name = json_find_first("$.data.list[0].name", data)
print(f"第一个设备名称: {first_name}")

# 添加 Artemis 前缀
url = "/api/parking/info"
prefixed_url = url_add_artemis_prefix(url)
print(f"带前缀的 URL: {prefixed_url}")  # 输出: /artemis/api/parking/info
```

### 响应模型使用

```python
from py_hikvision.isc import Isc
from py_hikvision.isc.responses import CODE_EQ_0
from py_hikvision.isc.utils import convert_to_code_eq_0

isc_inst = Isc(
    host="https://your-isc-server.com",
    ak="your-access-key",
    sk="your-secret-key"
)

response = isc_inst.request(method="GET", url="/api/parking/list")

# 转换为 CODE_EQ_0 模型
code_eq_0_response = convert_to_code_eq_0(response)

# 访问响应数据
print(f"状态码: {code_eq_0_response.code}")
print(f"消息: {code_eq_0_response.msg}")
print(f"数据: {code_eq_0_response.data.data}")
```

## API 文档

### Isc 类

#### 类定义

```python
class Isc:
    """
    Isc类，用于与iSecure Center进行交互
    
    根据海康威视官方API文档实现，支持AK/SK认证、同步/异步请求等核心功能。
    """
```

#### 初始化

```python
isc_inst = Isc(
    host: Optional[HttpUrl] = None,     # ISC 服务器地址，如 "https://isc.example.com"
    ak: Optional[str] = None,            # Access Key，用于 API 认证（对应官方文档的 appKey）
    sk: Optional[str] = None,            # Secret Key，用于生成请求签名（对应官方文档的 appSecret）
    client_kwargs: Optional[dict] = None # 传递给 httpx.Client 的额外配置参数
)
```

**参数说明**:

| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| host | HttpUrl | 否 | None | ISC 服务器地址，会自动去除末尾斜杠 |
| ak | str | 否 | None | Access Key，在运营中心创建合作方时获取 |
| sk | str | 否 | None | Secret Key，在运营中心创建合作方时获取 |
| client_kwargs | dict | 否 | None | httpx 客户端配置，如 timeout、verify 等 |

**默认配置**:
- `base_url`: host 值
- `verify`: False（跳过 SSL 证书验证）
- `timeout`: 60（60秒超时）

#### 方法说明

##### client()

创建并返回同步 HTTP 客户端。

```python
def client(self) -> httpx.Client:
    """
    创建并返回同步HTTP客户端
    
    返回:
        httpx.Client: 配置好的同步HTTP客户端实例
    """
```

**使用示例**:
```python
client = isc_inst.client()
response = client.get("/api/test")
```

##### async_client()

创建并返回异步 HTTP 客户端。

```python
def async_client(self) -> httpx.AsyncClient:
    """
    创建并返回异步HTTP客户端
    
    返回:
        httpx.AsyncClient: 配置好的异步HTTP客户端实例
    """
```

**使用示例**:
```python
async with isc_inst.async_client() as client:
    response = await client.get("/api/test")
```

##### signature(string)

使用 HMAC-SHA256 算法生成请求签名。

```python
def signature(self, string: str = "") -> str:
    """
    生成请求签名
    
    根据海康威视官方API规范，使用HMAC-SHA256算法对签名字符串进行加密。
    
    参数:
        string: 需要签名的字符串，由请求方法、Accept、Content-Type、
                x-ca-key、x-ca-nonce、x-ca-timestamp 和请求路径组成
    
    返回:
        str: 生成的签名（Base64编码的HMAC-SHA256哈希值）
    """
```

**签名算法流程**:
1. 将 Secret Key 编码为字节串作为 HMAC 密钥
2. 将待签名字符串编码为字节串
3. 使用 HMAC-SHA256 算法计算哈希值
4. 对哈希结果进行 Base64 编码并转换为字符串

**安全说明**:
- 签名机制防止请求被篡改
- 配合 nonce 和 timestamp 防止请求重放攻击

##### headers(method, path, headers)

生成符合 ISecureCenter API 规范的请求头。

```python
def headers(
    self,
    method: str = "POST",
    path: str = "",
    headers: dict = None
) -> dict:
    """
    生成符合 ISecureCenter API 规范的请求头
    
    根据ISC API规范生成包含认证信息的请求头，包括签名、密钥、随机数和时间戳。
    
    参数:
        method: HTTP请求方法，支持GET、POST、PUT、DELETE等，默认POST
        path: 请求路径，例如 "/api/parking/info"
        headers: 额外的请求头，会覆盖默认请求头中的同名项
    
    返回:
        dict: 完整的请求头字典，包含所有必要的认证信息
    """
```

**请求头字段说明**:

| 字段 | 说明 |
|------|------|
| accept | 接受所有响应类型，值为 `*/*` |
| content-type | 内容类型，固定为 `application/json` |
| x-ca-key | 访问密钥 Access Key，用于标识请求来源 |
| x-ca-nonce | 随机 UUID，防止请求被重放攻击 |
| x-ca-timestamp | 当前时间戳（毫秒），用于请求时效性验证 |
| x-ca-signature | 请求签名，使用 HMAC-SHA256 算法生成 |
| x-ca-signature-headers | 参与签名计算的请求头列表，值为 `x-ca-key,x-ca-nonce,x-ca-timestamp` |

**签名字符串格式**:

按顺序拼接以下字段，使用换行符分隔：
```
HTTP请求方法
Accept头值
Content-Type头值
x-ca-key:{value}
x-ca-nonce:{value}
x-ca-timestamp:{value}
请求路径
```

##### request(**kwargs)

发送同步 HTTP 请求。

```python
def request(self, client: Optional[httpx.Client] = None, **kwargs) -> httpx.Response:
    """
    发送同步 HTTP 请求
    
    自动添加 ISC API 认证签名，支持传入自定义客户端或使用内置客户端。
    
    参数:
        client: 自定义的 httpx.Client 实例，若为 None 则自动创建
        **kwargs: 传递给 httpx.Client.request 的参数，包括 method、url、headers、data、json 等
    
    返回:
        httpx.Response: HTTP 响应对象
    """
```

**使用示例**:
```python
# 基本用法
response = isc_inst.request(method="GET", url="/api/test")

# 带请求体
response = isc_inst.request(
    method="POST",
    url="/api/parking/info",
    json={"plateNo": "京A12345"}
)

# 使用自定义客户端
custom_client = httpx.Client(base_url="https://isc.example.com")
response = isc_inst.request(client=custom_client, method="GET", url="/api/test")
```

##### async_request(**kwargs)

发送异步 HTTP 请求。

```python
async def async_request(self, client: Optional[httpx.AsyncClient] = None, **kwargs) -> httpx.Response:
    """
    发送异步 HTTP 请求
    
    自动添加 ISC API 认证签名，支持传入自定义异步客户端或使用内置客户端。
    
    参数:
        client: 自定义的 httpx.AsyncClient 实例，若为 None 则自动创建
        **kwargs: 传递给 httpx.AsyncClient.request 的参数，包括 method、url、headers、data、json 等
    
    返回:
        httpx.Response: HTTP 响应对象
    """
```

**使用示例**:
```python
async def fetch_data():
    # 基本用法
    response = await isc_inst.async_request(method="GET", url="/api/test")
    
    # 带请求体
    response = await isc_inst.async_request(
        method="POST",
        url="/api/parking/info",
        json={"plateNo": "京A12345"}
    )
    return response
```

## 工具函数

### timestamp()

生成当前时间戳（毫秒）。

```python
def timestamp() -> int:
    """
    生成当前时间戳（毫秒）
    
    返回:
        int: 当前时间戳（毫秒），13位整数，用于请求的时效性验证
    """
```

**使用示例**:
```python
ts = timestamp()
print(ts)  # 输出示例: 1630000000000
```

### nonce()

生成随机的 UUID 字符串（无连字符）。

```python
def nonce() -> str:
    """
    生成随机的 UUID 字符串
    
    返回:
        str: 随机的 UUID 字符串（无连字符），32位十六进制字符串
    """
```

**使用示例**:
```python
random_nonce = nonce()
print(random_nonce)  # 输出示例: "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
```

### json_find_first(expression, data)

使用 JSONPath 表达式从数据中查找第一个匹配项。

```python
def json_find_first(expression: str, data: Any) -> Any:
    """
    使用 JSONPath 表达式从数据中查找第一个匹配项
    
    参数:
        expression: JSONPath 表达式，如 "$.data.list[0].name"
        data: 待查询的 JSON 数据
    
    返回:
        Any: 第一个匹配的值，如果没有匹配项则返回 None
    """
```

**使用示例**:
```python
data = {"code": 0, "data": {"list": [{"id": 1}, {"id": 2}]}}
first_id = json_find_first("$.data.list[0].id", data)
print(first_id)  # 输出: 1
```

### json_is_valid(schema, data)

校验 JSON 数据是否符合指定的 JSON Schema。

```python
def json_is_valid(schema: Optional[dict], data: Any) -> bool:
    """
    校验 JSON 数据是否符合指定的 JSON Schema
    
    参数:
        schema: JSON Schema 字典
        data: 待校验的 JSON 数据
    
    返回:
        bool: 校验是否成功
    """
```

**使用示例**:
```python
schema = {"type": "object", "properties": {"code": {"type": "integer"}}, "required": ["code"]}
is_valid = json_is_valid(schema, {"code": 0, "msg": "success"})
print(is_valid)  # 输出: True
```

### url_add_artemis_prefix(url)

在 URL 前添加 `/artemis/` 前缀。

```python
def url_add_artemis_prefix(url: Optional[str]) -> str:
    """
    在URL前添加/artemis/前缀
    
    根据海康威视ISC API规范，所有API请求路径必须以/artemis/开头。
    
    参数:
        url: 输入的URL字符串
    
    返回:
        str: 包含/artemis/前缀的URL字符串
    """
```

**使用示例**:
```python
url_add_artemis_prefix("/api/parking/info")  # 输出: "/artemis/api/parking/info"
url_add_artemis_prefix("api/test")           # 输出: "/artemis/api/test"
```

### image_to_base64_and_md5(image_path)

将图片文件转换为 Base64 编码和 MD5 校验值。

```python
def image_to_base64_and_md5(image_path: Optional[str] = None) -> Tuple[str, str]:
    """
    将图片文件转换为 Base64 编码和 MD5 校验值
    
    参数:
        image_path: 图片文件的路径
    
    返回:
        Tuple[str, str]: (base64编码字符串, MD5哈希值)
    
    抛出:
        FileNotFoundError: 图片路径不存在时
        IOError: 读取图片文件失败时
    """
```

**使用示例**:
```python
base64_str, md5_str = image_to_base64_and_md5("face.jpg")
print(f"Base64长度: {len(base64_str)}")
print(f"MD5: {md5_str}")
```

### convert_to_code_eq_0(response)

将 HTTP 响应或字典转换为 CODE_EQ_0 模型对象。

```python
def convert_to_code_eq_0(response: Union[httpx.Response, dict]) -> CODE_EQ_0:
    """
    将 HTTP 响应或字典转换为 CODE_EQ_0 模型对象
    
    参数:
        response: HTTP响应对象或字典数据
    
    返回:
        CODE_EQ_0: 标准化的响应模型对象
    """
```

**使用示例**:
```python
response = isc_inst.request(method="GET", url="/api/test")
code_eq_0 = convert_to_code_eq_0(response)
print(code_eq_0.code)   # 输出: 0
print(code_eq_0.data)   # 输出: {...}
```

### code_eq_0_validator(response, schema)

校验 HTTP 响应或字典是否符合指定的 JSON Schema。

```python
def code_eq_0_validator(
    response: Union[httpx.Response, dict] = None,
    schema: dict = {...}
) -> bool:
    """
    校验 HTTP 响应或字典是否符合指定的 JSON Schema
    
    默认校验 code 字段是否为 0（成功响应）。
    
    参数:
        response: HTTP响应对象或字典数据
        schema: JSON Schema 字典，默认为校验 code=0 的 Schema
    
    返回:
        bool: 校验是否成功
    """
```

**使用示例**:
```python
response = isc_inst.request(method="GET", url="/api/test")
is_valid = code_eq_0_validator(response)
print(is_valid)  # 输出: True
```

## 响应模型

### Base 类

所有 API 响应的基类。

```python
class Base(BaseModel):
    """
    iSecure Center响应基类
    
    字段:
        code: Union[int, str] - 错误码，0表示成功，非0表示失败
        msg: Optional[str] - 错误信息描述
    """
    code: Union[int, str] = Field(..., title="错误码", description="错误码，0表示成功，非0表示失败")
    msg: Optional[str] = Field(default=None, title="错误信息", description="错误信息描述")
    
    model_config = {"extra": "allow"}
```

### CODE_EQ_0 类

成功响应模型，继承自 Base。

```python
class CODE_EQ_0(Base):
    """
    成功响应模型
    
    根据海康威视ISC API规范，成功响应格式为：
    {"code": 0, "msg": "success", "data": {...}}
    
    字段:
        code: Literal[0, "0"] - 成功响应的错误码，固定为0
        msg: Optional[str] - 错误信息描述
        data: Any - 成功响应的数据内容
    """
    code: Literal[0, "0"] = Field(..., title="错误码", description="成功，值为0")
    data: Any = Field(title="数据", description="成功响应数据")
```

**使用示例**:
```python
from py_hikvision.isc.responses import CODE_EQ_0

# 直接创建
response = CODE_EQ_0(code=0, msg="success", data={"result": "ok"})

# 从字典转换
data = {"code": 0, "msg": "success", "data": {"list": []}}
response = CODE_EQ_0(**data)
```

## 安全说明

1. **签名机制**: 使用 HMAC-SHA256 算法确保请求完整性
2. **防止重放攻击**: 通过 nonce（随机 UUID）和 timestamp（时间戳）实现
3. **密钥管理**: Access Key 和 Secret Key 应妥善保管，避免泄露
4. **时间同步**: 客户端系统时间应与服务器时间同步，偏差超过 15 分钟会导致签名失效
5. **HTTPS**: 建议使用 HTTPS 协议传输，防止中间人攻击

## 依赖

| 依赖 | 版本要求 | 说明 |
|------|----------|------|
| httpx | >=0.27.0 | HTTP 客户端，支持同步和异步 |
| pydantic | >=2.0 | 数据验证和模型定义 |
| jsonpath-ng | >=1.5.3 | JSONPath 查询 |
| jsonschema | >=4.21.0 | JSON Schema 校验 |

## 开发依赖

| 依赖 | 版本要求 | 说明 |
|------|----------|------|
| pytest | >=7.0 | 测试框架 |
| pytest-cov | >=4.0 | 代码覆盖率 |
| pytest-asyncio | >=0.21.0 | 异步测试支持 |
| flake8 | >=6.0 | 代码风格检查 |
| black | >=23.0 | 代码格式化 |
| isort | >=5.12.0 | 导入排序 |

## 运行测试

```bash
# 安装开发依赖
pip install -e ".[dev]"

# 运行测试
pytest

# 运行测试并生成覆盖率报告
pytest --cov=py_hikvision --cov-report=html
```

## 官方文档

海康威视 iSecure Center API 官方文档:
- [https://open.hikvision.com/docs/docId?productId=5c67f1e2f05948198c909700&version=%2F29c78ef52ca842c7933bd2b8e051e9d0](https://open.hikvision.com/docs/docId?productId=5c67f1e2f05948198c909700&version=%2F29c78ef52ca842c7933bd2b8e051e9d0)

## 主页

[https://gitee.com/guolei19850528/py_hikvision](https://gitee.com/guolei19850528/py_hikvision)

## 许可证

MIT License

## 贡献

欢迎提交 Issue 和 Pull Request！

## 作者

Guolei <174000902@qq.com>
