een-api-toolkit 0.3.82 → 0.3.91
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/api-coverage-agent.md +264 -0
- package/.claude/agents/een-devices-agent.md +21 -0
- package/.claude/agents/een-ptz-agent.md +235 -0
- package/CHANGELOG.md +56 -60
- package/README.md +24 -1
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +400 -0
- package/dist/index.js +1079 -951
- package/dist/index.js.map +1 -1
- package/docs/AI-CONTEXT.md +3 -1
- package/docs/ai-reference/AI-AUTH.md +1 -1
- package/docs/ai-reference/AI-AUTOMATIONS.md +1 -1
- package/docs/ai-reference/AI-DEVICES.md +1 -1
- package/docs/ai-reference/AI-EVENT-DATA-SCHEMAS.md +1 -1
- package/docs/ai-reference/AI-EVENTS.md +1 -1
- package/docs/ai-reference/AI-GROUPING.md +1 -1
- package/docs/ai-reference/AI-JOBS.md +1 -1
- package/docs/ai-reference/AI-MEDIA.md +1 -1
- package/docs/ai-reference/AI-PTZ.md +158 -0
- package/docs/ai-reference/AI-SETUP.md +1 -1
- package/docs/ai-reference/AI-USERS.md +1 -1
- package/examples/vue-ptz/.env.example +4 -0
- package/examples/vue-ptz/README.md +221 -0
- package/examples/vue-ptz/e2e/app.spec.ts +58 -0
- package/examples/vue-ptz/e2e/auth.spec.ts +296 -0
- package/examples/vue-ptz/index.html +13 -0
- package/examples/vue-ptz/package-lock.json +1729 -0
- package/examples/vue-ptz/package.json +29 -0
- package/examples/vue-ptz/playwright.config.ts +49 -0
- package/examples/vue-ptz/screenshot-ptz.png +0 -0
- package/examples/vue-ptz/src/App.vue +154 -0
- package/examples/vue-ptz/src/components/ApiLog.vue +387 -0
- package/examples/vue-ptz/src/components/CameraSelector.vue +155 -0
- package/examples/vue-ptz/src/components/DirectionPad.vue +350 -0
- package/examples/vue-ptz/src/components/LiveVideoPlayer.vue +248 -0
- package/examples/vue-ptz/src/components/PositionDisplay.vue +206 -0
- package/examples/vue-ptz/src/components/PositionInput.vue +190 -0
- package/examples/vue-ptz/src/components/PresetManager.vue +538 -0
- package/examples/vue-ptz/src/composables/useApiLog.ts +89 -0
- package/examples/vue-ptz/src/main.ts +22 -0
- package/examples/vue-ptz/src/router/index.ts +61 -0
- package/examples/vue-ptz/src/views/Callback.vue +76 -0
- package/examples/vue-ptz/src/views/Home.vue +199 -0
- package/examples/vue-ptz/src/views/Login.vue +32 -0
- package/examples/vue-ptz/src/views/Logout.vue +59 -0
- package/examples/vue-ptz/src/views/PtzControl.vue +173 -0
- package/examples/vue-ptz/src/vite-env.d.ts +12 -0
- package/examples/vue-ptz/tsconfig.json +21 -0
- package/examples/vue-ptz/tsconfig.node.json +11 -0
- package/examples/vue-ptz/vite.config.ts +12 -0
- package/package.json +1 -1
package/docs/AI-CONTEXT.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# EEN API Toolkit - AI Reference
|
|
2
2
|
|
|
3
|
-
> **Version:** 0.3.
|
|
3
|
+
> **Version:** 0.3.91
|
|
4
4
|
>
|
|
5
5
|
> This documentation is optimized for AI assistants. It provides focused, domain-specific
|
|
6
6
|
> references to help you understand and use the een-api-toolkit efficiently.
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
| Events, alerts, metrics, SSE | [AI-EVENTS.md](./ai-reference/AI-EVENTS.md) | ~3.5K |
|
|
23
23
|
| Automation rules, alert actions | [AI-AUTOMATIONS.md](./ai-reference/AI-AUTOMATIONS.md) | ~4K |
|
|
24
24
|
| Jobs, exports, files, downloads | [AI-JOBS.md](./ai-reference/AI-JOBS.md) | ~3.5K |
|
|
25
|
+
| PTZ camera controls, presets | [AI-PTZ.md](./ai-reference/AI-PTZ.md) | ~2K |
|
|
25
26
|
|
|
26
27
|
## Specialized Agents
|
|
27
28
|
|
|
@@ -38,6 +39,7 @@ Specialized agents are available in `.claude/agents/` for domain-specific tasks:
|
|
|
38
39
|
| `een-events-agent` | Events, alerts, metrics, real-time SSE subscriptions |
|
|
39
40
|
| `een-automations-agent` | Automation rules, alert condition rules, alert actions |
|
|
40
41
|
| `een-jobs-agent` | Jobs, exports, files, downloads, video export workflows |
|
|
42
|
+
| `een-ptz-agent` | PTZ camera controls, presets, click-to-center |
|
|
41
43
|
|
|
42
44
|
**How to Use Agents:**
|
|
43
45
|
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# PTZ Camera Controls
|
|
2
|
+
|
|
3
|
+
> **Version:** 0.3.91
|
|
4
|
+
>
|
|
5
|
+
> Pan/Tilt/Zoom camera control: position, movement, presets, and automation.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## API Endpoints
|
|
10
|
+
|
|
11
|
+
| Function | Method | Endpoint | Returns |
|
|
12
|
+
|----------|--------|----------|---------|
|
|
13
|
+
| `getPtzPosition(cameraId)` | GET | `/cameras/{cameraId}/ptz/position` | `Result<PtzPosition>` |
|
|
14
|
+
| `movePtz(cameraId, move)` | PUT | `/cameras/{cameraId}/ptz/position` | `Result<void>` |
|
|
15
|
+
| `getPtzSettings(cameraId)` | GET | `/cameras/{cameraId}/ptz/settings` | `Result<PtzSettings>` |
|
|
16
|
+
| `updatePtzSettings(cameraId, settings)` | PATCH | `/cameras/{cameraId}/ptz/settings` | `Result<void>` |
|
|
17
|
+
|
|
18
|
+
## Types
|
|
19
|
+
|
|
20
|
+
### PtzPosition
|
|
21
|
+
```typescript
|
|
22
|
+
interface PtzPosition {
|
|
23
|
+
x?: number // Pan (horizontal)
|
|
24
|
+
y?: number // Tilt (vertical)
|
|
25
|
+
z?: number // Zoom level
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### PtzMove (discriminated union)
|
|
30
|
+
```typescript
|
|
31
|
+
type PtzMove = PtzPositionMove | PtzDirectionMove | PtzCenterOnMove
|
|
32
|
+
|
|
33
|
+
// Absolute position
|
|
34
|
+
interface PtzPositionMove {
|
|
35
|
+
moveType: 'position'
|
|
36
|
+
x?: number; y?: number; z?: number
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Relative direction
|
|
40
|
+
interface PtzDirectionMove {
|
|
41
|
+
moveType: 'direction'
|
|
42
|
+
direction: PtzDirection[] // 'up' | 'down' | 'left' | 'right' | 'in' | 'out'
|
|
43
|
+
stepSize?: PtzStepSize // 'small' | 'medium' | 'large'
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Center on point in frame
|
|
47
|
+
interface PtzCenterOnMove {
|
|
48
|
+
moveType: 'centerOn'
|
|
49
|
+
relativeX: number // 0.0 (left) to 1.0 (right)
|
|
50
|
+
relativeY: number // 0.0 (top) to 1.0 (bottom)
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### PtzSettings & PtzSettingsUpdate
|
|
55
|
+
```typescript
|
|
56
|
+
interface PtzSettings {
|
|
57
|
+
presets: PtzPreset[]
|
|
58
|
+
homePreset: string | null
|
|
59
|
+
mode: PtzMode // 'homeReturn' | 'tour' | 'manualOnly'
|
|
60
|
+
autoStartDelay: number // seconds
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
interface PtzPreset {
|
|
64
|
+
name: string
|
|
65
|
+
position: PtzPosition
|
|
66
|
+
timeAtPreset: number // seconds at preset during tour
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
interface PtzSettingsUpdate {
|
|
70
|
+
presets?: PtzPreset[]
|
|
71
|
+
homePreset?: string | null
|
|
72
|
+
mode?: PtzMode
|
|
73
|
+
autoStartDelay?: number
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Usage Examples
|
|
78
|
+
|
|
79
|
+
### Get Position
|
|
80
|
+
```typescript
|
|
81
|
+
import { getPtzPosition } from 'een-api-toolkit'
|
|
82
|
+
|
|
83
|
+
const { data, error } = await getPtzPosition('camera-123')
|
|
84
|
+
if (data) {
|
|
85
|
+
console.log(`Pan: ${data.x}, Tilt: ${data.y}, Zoom: ${data.z}`)
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Move Camera
|
|
90
|
+
```typescript
|
|
91
|
+
import { movePtz } from 'een-api-toolkit'
|
|
92
|
+
|
|
93
|
+
// Absolute position
|
|
94
|
+
await movePtz('camera-123', { moveType: 'position', x: 0.5, y: -0.3, z: 2.0 })
|
|
95
|
+
|
|
96
|
+
// Direction with step size
|
|
97
|
+
await movePtz('camera-123', {
|
|
98
|
+
moveType: 'direction', direction: ['up', 'left'], stepSize: 'medium'
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
// Click-to-center on video frame
|
|
102
|
+
await movePtz('camera-123', { moveType: 'centerOn', relativeX: 0.75, relativeY: 0.5 })
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Manage Presets
|
|
106
|
+
```typescript
|
|
107
|
+
import { getPtzSettings, updatePtzSettings, getPtzPosition } from 'een-api-toolkit'
|
|
108
|
+
|
|
109
|
+
// Read presets
|
|
110
|
+
const { data: settings } = await getPtzSettings('camera-123')
|
|
111
|
+
|
|
112
|
+
// Save current position as preset
|
|
113
|
+
const { data: pos } = await getPtzPosition('camera-123')
|
|
114
|
+
if (pos && settings) {
|
|
115
|
+
await updatePtzSettings('camera-123', {
|
|
116
|
+
presets: [...settings.presets, { name: 'Gate', position: pos, timeAtPreset: 10 }],
|
|
117
|
+
homePreset: 'Gate'
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Change mode
|
|
122
|
+
await updatePtzSettings('camera-123', { mode: 'tour', autoStartDelay: 30 })
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Click-to-Center Pattern
|
|
126
|
+
```typescript
|
|
127
|
+
function handleVideoClick(event: MouseEvent) {
|
|
128
|
+
const video = event.currentTarget as HTMLVideoElement
|
|
129
|
+
const rect = video.getBoundingClientRect()
|
|
130
|
+
const relativeX = (event.clientX - rect.left) / rect.width
|
|
131
|
+
const relativeY = (event.clientY - rect.top) / rect.height
|
|
132
|
+
movePtz(cameraId, { moveType: 'centerOn', relativeX, relativeY })
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Error Handling
|
|
137
|
+
|
|
138
|
+
| Error Code | Meaning | Action |
|
|
139
|
+
|------------|---------|--------|
|
|
140
|
+
| AUTH_REQUIRED | Not authenticated | Redirect to login |
|
|
141
|
+
| NOT_FOUND | Camera not found or no PTZ | Show message |
|
|
142
|
+
| FORBIDDEN | No permission | Show access denied |
|
|
143
|
+
| VALIDATION_ERROR | Empty camera ID | Fix input |
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Reference Examples
|
|
148
|
+
|
|
149
|
+
- `examples/vue-ptz/` - Complete PTZ control app with live video
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
## See Also
|
|
153
|
+
|
|
154
|
+
- [AI-AUTH.md](./AI-AUTH.md)
|
|
155
|
+
- [AI-DEVICES.md](./AI-DEVICES.md)
|
|
156
|
+
- [AI-MEDIA.md](./AI-MEDIA.md)
|
|
157
|
+
|
|
158
|
+
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# EEN API Toolkit - Vue PTZ Example
|
|
2
|
+
|
|
3
|
+
A Vue 3 example demonstrating PTZ (Pan/Tilt/Zoom) camera control using the een-api-toolkit, including live video streaming, directional movement, click-to-center, absolute positioning, and preset management.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
## Storage Strategy: sessionStorage
|
|
8
|
+
|
|
9
|
+
This example uses the `sessionStorage` storage strategy for balanced security. This means:
|
|
10
|
+
|
|
11
|
+
- **Per-tab isolation** - each browser tab has its own session
|
|
12
|
+
- **Page refresh preserves session** - tokens survive refresh within the same tab
|
|
13
|
+
- **Tab close clears session** - closing the tab removes tokens
|
|
14
|
+
- **New tabs require login** - opening a new tab requires separate authentication
|
|
15
|
+
|
|
16
|
+
This is a good balance between security (limiting XSS blast radius) and user experience (page refresh doesn't require re-login).
|
|
17
|
+
|
|
18
|
+
## Features Demonstrated
|
|
19
|
+
|
|
20
|
+
- OAuth authentication flow (login, callback, logout)
|
|
21
|
+
- Protected routes with navigation guards
|
|
22
|
+
- PTZ camera discovery (filters cameras by PTZ capability)
|
|
23
|
+
- Live video streaming via `@een/live-video-web-sdk`
|
|
24
|
+
- Click-to-center on the live video feed
|
|
25
|
+
- Direction pad with pan/tilt/zoom controls
|
|
26
|
+
- Absolute position input (pan, tilt, zoom, field of view)
|
|
27
|
+
- Current position display with auto-refresh
|
|
28
|
+
- Preset management (list, recall, create, delete, set home)
|
|
29
|
+
- Real-time API call logging
|
|
30
|
+
|
|
31
|
+
## Pages Overview
|
|
32
|
+
|
|
33
|
+
### Home Page
|
|
34
|
+
The landing page that displays a welcome message and login prompt when not authenticated. Shows the available toolkit functions and their descriptions.
|
|
35
|
+
|
|
36
|
+
### PTZ Control
|
|
37
|
+
The main control page with a two-column layout: live video on the left and PTZ controls on the right.
|
|
38
|
+
|
|
39
|
+
**Components:**
|
|
40
|
+
|
|
41
|
+
- **CameraSelector** - Discovers PTZ-capable cameras by checking each camera's capabilities and auto-selects the first one
|
|
42
|
+
- **LiveVideoPlayer** - Streams live video using the EEN Live Video Web SDK with click-to-center support
|
|
43
|
+
- **PositionDisplay** - Shows the current pan/tilt/zoom/field-of-view values with auto-refresh
|
|
44
|
+
- **DirectionPad** - Provides directional movement buttons (up/down/left/right, zoom in/out) and a home button
|
|
45
|
+
- **PositionInput** - Allows entering absolute pan/tilt/zoom/FOV values to move the camera
|
|
46
|
+
- **PresetManager** - Lists, recalls, creates, and deletes PTZ presets; designates a home preset
|
|
47
|
+
- **ApiLog** - Displays a real-time log of API calls made by the toolkit
|
|
48
|
+
|
|
49
|
+
## APIs Used Summary
|
|
50
|
+
|
|
51
|
+
| API Function | Component | Purpose |
|
|
52
|
+
|--------------|-----------|---------|
|
|
53
|
+
| `getCameras()` | CameraSelector | List available cameras |
|
|
54
|
+
| `getCamera()` | CameraSelector | Check PTZ capability per camera |
|
|
55
|
+
| `getPtzPosition()` | PositionDisplay | Read current camera position |
|
|
56
|
+
| `movePtz()` | DirectionPad, PositionInput, LiveVideoPlayer, PresetManager | Move camera (direction, position, centerOn) |
|
|
57
|
+
| `getPtzSettings()` | PresetManager | Retrieve presets and automation settings |
|
|
58
|
+
| `updatePtzSettings()` | PresetManager | Create/delete presets, set home preset |
|
|
59
|
+
| `getCurrentUser()` | App.vue | Fetch logged-in user profile for header display |
|
|
60
|
+
| `useAuthStore()` | Multiple | Authentication state management |
|
|
61
|
+
| `getAuthUrl()` | Login | Generate OAuth login URL |
|
|
62
|
+
| `handleAuthCallback()` | Callback | Process OAuth callback |
|
|
63
|
+
| `revokeToken()` | Logout | Revoke authentication token on logout |
|
|
64
|
+
| `initEenToolkit()` | App initialization | Configure toolkit settings |
|
|
65
|
+
| `getStorageStrategy()` | Home | Get the current storage strategy |
|
|
66
|
+
| `STORAGE_STRATEGY_DESCRIPTIONS` | Home | Human-readable storage strategy descriptions |
|
|
67
|
+
|
|
68
|
+
## Setup
|
|
69
|
+
|
|
70
|
+
### Prerequisites
|
|
71
|
+
|
|
72
|
+
1. **Start the OAuth proxy** (required for authentication):
|
|
73
|
+
|
|
74
|
+
The OAuth proxy is a separate project that handles token management securely.
|
|
75
|
+
Clone and run it from: https://github.com/klaushofrichter/een-oauth-proxy
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# In a separate terminal, from the een-oauth-proxy directory
|
|
79
|
+
npm install
|
|
80
|
+
npm run dev
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The proxy should be running at `http://localhost:8787`.
|
|
84
|
+
|
|
85
|
+
### Example Setup
|
|
86
|
+
|
|
87
|
+
All commands below should be run from this example directory (`examples/vue-ptz/`):
|
|
88
|
+
|
|
89
|
+
2. Copy the environment file:
|
|
90
|
+
```bash
|
|
91
|
+
# From examples/vue-ptz/
|
|
92
|
+
cp .env.example .env
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
3. Edit `.env` with your EEN credentials:
|
|
96
|
+
```env
|
|
97
|
+
VITE_EEN_CLIENT_ID=your-client-id
|
|
98
|
+
VITE_PROXY_URL=http://127.0.0.1:8787
|
|
99
|
+
# DO NOT change the redirect URI - EEN IDP only permits this URL
|
|
100
|
+
VITE_REDIRECT_URI=http://127.0.0.1:3333
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
4. Install dependencies and start:
|
|
104
|
+
```bash
|
|
105
|
+
# From examples/vue-ptz/
|
|
106
|
+
npm install
|
|
107
|
+
npm run dev
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
5. Open http://127.0.0.1:3333 in your browser.
|
|
111
|
+
|
|
112
|
+
**Important:** The EEN Identity Provider only permits `http://127.0.0.1:3333` as the OAuth redirect URI. Do not use `localhost` or other ports.
|
|
113
|
+
|
|
114
|
+
**Note:** Development and testing was done on macOS. The `npm run stop` command uses `lsof`, which is not available on Windows. Windows users should manually stop any process on port 3333 or use `npx kill-port 3333` instead.
|
|
115
|
+
|
|
116
|
+
## Project Structure
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
src/
|
|
120
|
+
├── main.ts # App entry, toolkit initialization
|
|
121
|
+
├── App.vue # Root component with navigation and user info
|
|
122
|
+
├── router/
|
|
123
|
+
│ └── index.ts # Vue Router with auth guards
|
|
124
|
+
├── composables/
|
|
125
|
+
│ └── useApiLog.ts # Shared API call log state
|
|
126
|
+
├── components/
|
|
127
|
+
│ ├── CameraSelector.vue # PTZ camera discovery and selection
|
|
128
|
+
│ ├── LiveVideoPlayer.vue # Live video stream with click-to-center
|
|
129
|
+
│ ├── DirectionPad.vue # Directional movement controls
|
|
130
|
+
│ ├── PositionDisplay.vue # Current position readout
|
|
131
|
+
│ ├── PositionInput.vue # Absolute position input form
|
|
132
|
+
│ ├── PresetManager.vue # Preset CRUD and home preset
|
|
133
|
+
│ └── ApiLog.vue # Real-time API call log
|
|
134
|
+
└── views/
|
|
135
|
+
├── Home.vue # Home page with login prompt
|
|
136
|
+
├── Login.vue # OAuth login redirect
|
|
137
|
+
├── Callback.vue # OAuth callback handler
|
|
138
|
+
├── PtzControl.vue # Main PTZ control page
|
|
139
|
+
└── Logout.vue # Logout handler
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Key Code Examples
|
|
143
|
+
|
|
144
|
+
### Moving a PTZ Camera (DirectionPad.vue)
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
import { movePtz } from 'een-api-toolkit'
|
|
148
|
+
|
|
149
|
+
// Move camera using relative direction
|
|
150
|
+
await movePtz(cameraId, {
|
|
151
|
+
moveType: 'direction',
|
|
152
|
+
direction: ['right'],
|
|
153
|
+
stepSize: 'medium'
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
// Move camera to absolute position
|
|
157
|
+
await movePtz(cameraId, {
|
|
158
|
+
moveType: 'position',
|
|
159
|
+
x: 0.5, y: -0.3, z: 2.0
|
|
160
|
+
})
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Click-to-Center (LiveVideoPlayer.vue)
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
import { movePtz } from 'een-api-toolkit'
|
|
167
|
+
|
|
168
|
+
// Click coordinates on the video trigger centerOn move (0.0 to 1.0)
|
|
169
|
+
const relativeX = (event.clientX - rect.left) / rect.width
|
|
170
|
+
const relativeY = (event.clientY - rect.top) / rect.height
|
|
171
|
+
await movePtz(cameraId, { moveType: 'centerOn', relativeX, relativeY })
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Managing Presets (PresetManager.vue)
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
import { getPtzSettings, updatePtzSettings, getPtzPosition } from 'een-api-toolkit'
|
|
178
|
+
|
|
179
|
+
// List presets
|
|
180
|
+
const result = await getPtzSettings(cameraId)
|
|
181
|
+
const presets = result.data?.presets || []
|
|
182
|
+
|
|
183
|
+
// Recall a preset (move to its saved position)
|
|
184
|
+
await movePtz(cameraId, { moveType: 'position', x: preset.position.x, y: preset.position.y, z: preset.position.z })
|
|
185
|
+
|
|
186
|
+
// Save current position as a new preset (fetch existing presets first to avoid overwriting)
|
|
187
|
+
const { data: pos } = await getPtzPosition(cameraId)
|
|
188
|
+
if (pos && result.data) {
|
|
189
|
+
await updatePtzSettings(cameraId, {
|
|
190
|
+
presets: [...result.data.presets, { name: 'New Preset', position: pos, timeAtPreset: 10 }]
|
|
191
|
+
})
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Reading Camera Position (PositionDisplay.vue)
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
import { getPtzPosition } from 'een-api-toolkit'
|
|
199
|
+
|
|
200
|
+
const result = await getPtzPosition(cameraId)
|
|
201
|
+
if (result.data) {
|
|
202
|
+
const { x, y, z } = result.data // pan, tilt, zoom
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### PTZ Camera Discovery (CameraSelector.vue)
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
import { getCameras } from 'een-api-toolkit'
|
|
210
|
+
|
|
211
|
+
// Fetch all cameras with capabilities in a single paginated request
|
|
212
|
+
const ptzCameras = []
|
|
213
|
+
let pageToken: string | undefined
|
|
214
|
+
do {
|
|
215
|
+
const result = await getCameras({ pageSize: 100, include: ['capabilities'], pageToken })
|
|
216
|
+
for (const cam of result.data?.results || []) {
|
|
217
|
+
if (cam.capabilities?.ptz?.capable) ptzCameras.push(cam)
|
|
218
|
+
}
|
|
219
|
+
pageToken = result.data?.nextPageToken ?? undefined
|
|
220
|
+
} while (pageToken)
|
|
221
|
+
```
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { test, expect } from '@playwright/test'
|
|
2
|
+
|
|
3
|
+
test.describe('vue-ptz example app', () => {
|
|
4
|
+
test('home page shows login button when not authenticated', async ({ page }) => {
|
|
5
|
+
await page.goto('/')
|
|
6
|
+
|
|
7
|
+
// Should show the home page
|
|
8
|
+
await expect(page.getByRole('heading', { name: 'Welcome to the EEN PTZ Control Example' })).toBeVisible()
|
|
9
|
+
|
|
10
|
+
// Should show "not authenticated" state with login button
|
|
11
|
+
await expect(page.getByTestId('not-authenticated')).toBeVisible()
|
|
12
|
+
await expect(page.getByTestId('login-button')).toBeVisible()
|
|
13
|
+
await expect(page.getByText('Please log in to control PTZ cameras')).toBeVisible()
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
test('login button navigates to login page', async ({ page }) => {
|
|
17
|
+
await page.goto('/')
|
|
18
|
+
|
|
19
|
+
await page.getByTestId('login-button').click()
|
|
20
|
+
|
|
21
|
+
await expect(page).toHaveURL('/login')
|
|
22
|
+
await expect(page.getByRole('heading', { name: 'Login' })).toBeVisible()
|
|
23
|
+
await expect(page.getByText('Click the button below to authenticate with Eagle Eye Networks')).toBeVisible()
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test('ptz route redirects to login when not authenticated', async ({ page }) => {
|
|
27
|
+
await page.goto('/ptz')
|
|
28
|
+
|
|
29
|
+
// Should redirect to login page (auth guard)
|
|
30
|
+
await expect(page).toHaveURL('/login')
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test('navigation links work correctly', async ({ page }) => {
|
|
34
|
+
await page.goto('/')
|
|
35
|
+
|
|
36
|
+
// Check navigation is present
|
|
37
|
+
await expect(page.getByRole('navigation')).toBeVisible()
|
|
38
|
+
|
|
39
|
+
// Navigate to login via nav link (use testid to be specific)
|
|
40
|
+
await page.getByTestId('nav-login').click()
|
|
41
|
+
await expect(page).toHaveURL('/login')
|
|
42
|
+
|
|
43
|
+
// Navigate back home
|
|
44
|
+
await page.getByRole('link', { name: 'Home' }).click()
|
|
45
|
+
await expect(page).toHaveURL('/')
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('about section displays toolkit function list', async ({ page }) => {
|
|
49
|
+
await page.goto('/')
|
|
50
|
+
|
|
51
|
+
// Check for the function descriptions
|
|
52
|
+
await expect(page.getByText('getCameras()')).toBeVisible()
|
|
53
|
+
await expect(page.getByText('getPtzPosition()')).toBeVisible()
|
|
54
|
+
await expect(page.getByText('movePtz()')).toBeVisible()
|
|
55
|
+
await expect(page.getByText('getPtzSettings()')).toBeVisible()
|
|
56
|
+
await expect(page.getByText('updatePtzSettings()')).toBeVisible()
|
|
57
|
+
})
|
|
58
|
+
})
|