|
|
@@ -2,6 +2,8 @@
|
|
|
* PTZ 云台控制 API
|
|
|
* 通过独立的 PTZ 后端服务调用 ONVIF 标准协议
|
|
|
* 支持厂家: HIKVISION (海康威视), ANPVIZ
|
|
|
+ *
|
|
|
+ * 注意: 用户名和密码存储在后端服务器,前端不传递凭据
|
|
|
*/
|
|
|
|
|
|
// ==================== 类型定义 ====================
|
|
|
@@ -11,8 +13,6 @@ export type CameraVendor = 'HIKVISION' | 'ANPVIZ' | 'DAHUA' | 'CT-IP500' | 'SVBC
|
|
|
|
|
|
export interface PTZConfig {
|
|
|
host: string
|
|
|
- username: string
|
|
|
- password: string
|
|
|
channel?: number
|
|
|
cameraId?: number
|
|
|
vendor?: CameraVendor
|
|
|
@@ -87,8 +87,6 @@ async function sendPTZCommand(
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
body: JSON.stringify({
|
|
|
host: config.host,
|
|
|
- username: config.username,
|
|
|
- password: config.password,
|
|
|
channel: config.channel || 1,
|
|
|
cameraId: config.cameraId,
|
|
|
vendor: config.vendor || 'HIKVISION',
|
|
|
@@ -117,8 +115,6 @@ async function sendCommand(config: PTZConfig, command: PTZCommand): Promise<PTZR
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
body: JSON.stringify({
|
|
|
host: config.host,
|
|
|
- username: config.username,
|
|
|
- password: config.password,
|
|
|
channel: config.channel || 1,
|
|
|
cameraId: config.cameraId,
|
|
|
vendor: config.vendor || 'HIKVISION',
|
|
|
@@ -147,8 +143,6 @@ export async function getPTZStatus(config: PTZConfig): Promise<PTZResult & { dat
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
body: JSON.stringify({
|
|
|
host: config.host,
|
|
|
- username: config.username,
|
|
|
- password: config.password,
|
|
|
channel: config.channel || 1
|
|
|
})
|
|
|
})
|
|
|
@@ -227,3 +221,173 @@ export function stopZoom(config: PTZConfig): Promise<PTZResult> {
|
|
|
export const zoomIn = (config: PTZConfig, speed?: number) => startZoom(config, 'IN', speed)
|
|
|
export const zoomOut = (config: PTZConfig, speed?: number) => startZoom(config, 'OUT', speed)
|
|
|
export const zoomStop = stopZoom
|
|
|
+
|
|
|
+// ==================== 预置位 API ====================
|
|
|
+
|
|
|
+// 预置位信息
|
|
|
+export interface PTZPresetInfo {
|
|
|
+ id: string
|
|
|
+ name: string
|
|
|
+}
|
|
|
+
|
|
|
+// PTZ 能力信息
|
|
|
+export interface PTZCapabilities {
|
|
|
+ absolutePanTilt?: {
|
|
|
+ panMin: number
|
|
|
+ panMax: number
|
|
|
+ tiltMin: number
|
|
|
+ tiltMax: number
|
|
|
+ }
|
|
|
+ absoluteZoom?: {
|
|
|
+ min: number
|
|
|
+ max: number
|
|
|
+ }
|
|
|
+ continuousPanTilt?: {
|
|
|
+ panMin: number
|
|
|
+ panMax: number
|
|
|
+ tiltMin: number
|
|
|
+ tiltMax: number
|
|
|
+ }
|
|
|
+ continuousZoom?: {
|
|
|
+ min: number
|
|
|
+ max: number
|
|
|
+ }
|
|
|
+ maxPresetNum?: number
|
|
|
+ controlProtocol?: {
|
|
|
+ options: string[]
|
|
|
+ current: string
|
|
|
+ }
|
|
|
+ specialPresetIds?: number[]
|
|
|
+ presetName?: {
|
|
|
+ supported: boolean
|
|
|
+ maxLength: number
|
|
|
+ }
|
|
|
+ support3DPosition?: boolean
|
|
|
+ supportPtzLimits?: boolean
|
|
|
+}
|
|
|
+
|
|
|
+// 摄像头连接参数 (与 PTZConfig 兼容)
|
|
|
+export type CameraConnection = PTZConfig
|
|
|
+
|
|
|
+/**
|
|
|
+ * 获取预置位列表
|
|
|
+ */
|
|
|
+export async function getPTZPresets(config: PTZConfig): Promise<PTZResult & { data?: PTZPresetInfo[] }> {
|
|
|
+ try {
|
|
|
+ const response = await fetch(`${PTZ_API_BASE}/preset/list`, {
|
|
|
+ method: 'POST',
|
|
|
+ headers: { 'Content-Type': 'application/json' },
|
|
|
+ body: JSON.stringify({
|
|
|
+ host: config.host,
|
|
|
+ channel: config.channel || 1
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ const data = await response.json()
|
|
|
+ return data.code === 200
|
|
|
+ ? { success: true, data: data.data }
|
|
|
+ : { success: false, error: data.msg || 'Unknown error' }
|
|
|
+ } catch (error) {
|
|
|
+ return { success: false, error: String(error) }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 跳转到预置位
|
|
|
+ */
|
|
|
+export async function gotoPTZPreset(config: PTZConfig, presetId: number): Promise<PTZResult> {
|
|
|
+ try {
|
|
|
+ const response = await fetch(`${PTZ_API_BASE}/preset/goto`, {
|
|
|
+ method: 'POST',
|
|
|
+ headers: { 'Content-Type': 'application/json' },
|
|
|
+ body: JSON.stringify({
|
|
|
+ host: config.host,
|
|
|
+ channel: config.channel || 1,
|
|
|
+ presetId
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ const data = await response.json()
|
|
|
+ return data.code === 200
|
|
|
+ ? { success: true, data: data.data }
|
|
|
+ : { success: false, error: data.msg || 'Unknown error' }
|
|
|
+ } catch (error) {
|
|
|
+ return { success: false, error: String(error) }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 设置预置位 (保存当前位置)
|
|
|
+ */
|
|
|
+export async function setPTZPreset(
|
|
|
+ config: PTZConfig,
|
|
|
+ presetId: number,
|
|
|
+ presetName?: string
|
|
|
+): Promise<PTZResult & { data?: { presetId: number; presetName: string } }> {
|
|
|
+ try {
|
|
|
+ const response = await fetch(`${PTZ_API_BASE}/preset/set`, {
|
|
|
+ method: 'POST',
|
|
|
+ headers: { 'Content-Type': 'application/json' },
|
|
|
+ body: JSON.stringify({
|
|
|
+ host: config.host,
|
|
|
+ channel: config.channel || 1,
|
|
|
+ presetId,
|
|
|
+ presetName
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ const data = await response.json()
|
|
|
+ return data.code === 200
|
|
|
+ ? { success: true, data: data.data }
|
|
|
+ : { success: false, error: data.msg || 'Unknown error' }
|
|
|
+ } catch (error) {
|
|
|
+ return { success: false, error: String(error) }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 删除预置位
|
|
|
+ */
|
|
|
+export async function removePTZPreset(config: PTZConfig, presetId: number): Promise<PTZResult> {
|
|
|
+ try {
|
|
|
+ const response = await fetch(`${PTZ_API_BASE}/preset/remove`, {
|
|
|
+ method: 'POST',
|
|
|
+ headers: { 'Content-Type': 'application/json' },
|
|
|
+ body: JSON.stringify({
|
|
|
+ host: config.host,
|
|
|
+ channel: config.channel || 1,
|
|
|
+ presetId
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ const data = await response.json()
|
|
|
+ return data.code === 200
|
|
|
+ ? { success: true, data: data.data }
|
|
|
+ : { success: false, error: data.msg || 'Unknown error' }
|
|
|
+ } catch (error) {
|
|
|
+ return { success: false, error: String(error) }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 获取 PTZ 能力参数
|
|
|
+ */
|
|
|
+export async function getPTZCapabilities(config: PTZConfig): Promise<PTZResult & { data?: PTZCapabilities }> {
|
|
|
+ try {
|
|
|
+ const response = await fetch(`${PTZ_API_BASE}/ptz/capabilities`, {
|
|
|
+ method: 'POST',
|
|
|
+ headers: { 'Content-Type': 'application/json' },
|
|
|
+ body: JSON.stringify({
|
|
|
+ host: config.host,
|
|
|
+ channel: config.channel || 1
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ const data = await response.json()
|
|
|
+ return data.code === 200
|
|
|
+ ? { success: true, data: data.data }
|
|
|
+ : { success: false, error: data.msg || 'Unknown error' }
|
|
|
+ } catch (error) {
|
|
|
+ return { success: false, error: String(error) }
|
|
|
+ }
|
|
|
+}
|