een-api-toolkit 0.3.85 → 0.3.97
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 +71 -29
- package/.claude/agents/een-devices-agent.md +21 -0
- package/.claude/agents/een-ptz-agent.md +245 -0
- package/CHANGELOG.md +60 -40
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +412 -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 +12 -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 +174 -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
|
@@ -205,40 +205,82 @@ Implemented: [brief list of what IS implemented]
|
|
|
205
205
|
|
|
206
206
|
#### Document 4: `docs/een-api-coverage.html`
|
|
207
207
|
|
|
208
|
-
Generate a self-contained HTML file
|
|
209
|
-
|
|
210
|
-
**
|
|
211
|
-
-
|
|
212
|
-
-
|
|
213
|
-
-
|
|
214
|
-
|
|
215
|
-
**
|
|
216
|
-
-
|
|
217
|
-
-
|
|
218
|
-
-
|
|
219
|
-
-
|
|
220
|
-
|
|
221
|
-
**
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
-
|
|
225
|
-
-
|
|
226
|
-
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
-
|
|
233
|
-
-
|
|
208
|
+
Generate a self-contained HTML file matching this exact visual style:
|
|
209
|
+
|
|
210
|
+
**Global reset and body**:
|
|
211
|
+
- `*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }`
|
|
212
|
+
- Font: `-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, sans-serif`
|
|
213
|
+
- Background: `#f5f7fa`, color: `#1a202c`
|
|
214
|
+
|
|
215
|
+
**Header** (`<header>`):
|
|
216
|
+
- Dark navy background: `#1a365d`, white text
|
|
217
|
+
- Padding: `24px 32px`
|
|
218
|
+
- `<h1>` at `1.5rem` bold: "Eagle Eye Networks API v3.0 — Toolkit Coverage"
|
|
219
|
+
- `<p>` at `0.875rem`, color `#bee3f8`: generation date and source info
|
|
220
|
+
|
|
221
|
+
**Container**: `max-width: 100%` (use full page width), centered, padding `24px 32px`
|
|
222
|
+
|
|
223
|
+
**Stat cards** (`.stats` grid):
|
|
224
|
+
- `grid-template-columns: repeat(auto-fit, minmax(180px, 1fr))`, gap `16px`
|
|
225
|
+
- Each card: white background, `border-radius: 8px`, padding `20px`, `box-shadow: 0 1px 3px rgba(0,0,0,.08)`
|
|
226
|
+
- Colored `border-top: 4px solid` — blue (`#4299e1`) for Total, green (`#48bb78`) for Implemented, red (`#fc8181`) for Missing, indigo (`#667eea`) for Coverage
|
|
227
|
+
- Label: `0.75rem` uppercase, `letter-spacing: .05em`, color `#718096`
|
|
228
|
+
- Value: `2rem` bold, color `#2d3748`
|
|
229
|
+
- Sub text: `0.8rem`, color `#718096`
|
|
230
|
+
|
|
231
|
+
**Progress bar** (`.progress-wrap`):
|
|
232
|
+
- White card with same shadow/radius as stat cards
|
|
233
|
+
- Label row: flex with `justify-content: space-between`, `0.875rem`, color `#4a5568`
|
|
234
|
+
- Bar background: `#e2e8f0`, `border-radius: 9999px`, height `12px`
|
|
235
|
+
- Fill: `linear-gradient(90deg, #48bb78, #38a169)`, same radius, `transition: width .4s ease`
|
|
236
|
+
|
|
237
|
+
**Filter bar** (`.filter-bar`):
|
|
238
|
+
- White card, flex with `flex-wrap: wrap`, gap `12px`
|
|
239
|
+
- Labels: `0.8rem`, color `#718096`
|
|
240
|
+
- Inputs/selects: border `1px solid #e2e8f0`, `border-radius: 6px`, padding `6px 10px`, `0.875rem`
|
|
241
|
+
- Focus: `border-color: #4299e1`, `box-shadow: 0 0 0 3px rgba(66,153,225,.15)`
|
|
242
|
+
- Text input width: `220px`
|
|
243
|
+
- Filter count: `margin-left: auto`, `0.8rem`, color `#718096`
|
|
244
|
+
- Dropdowns: Status (All/Implemented/Missing), Category (All + each category), Method (All/GET/POST/PATCH/DELETE/PUT)
|
|
245
|
+
- Text search placeholder: "path, function, description..."
|
|
246
|
+
|
|
247
|
+
**Table** (`.table-wrap`):
|
|
248
|
+
- White card with `overflow-x: auto` for horizontal scrollbar on narrow viewports
|
|
249
|
+
- The `<table>` element must have `min-width: 1200px` so content is never clipped — the scrollbar activates instead of truncating columns
|
|
250
|
+
- `<thead>`: dark background `#2d3748`, white text, uppercase `0.75rem`, `letter-spacing: .05em`
|
|
251
|
+
- Headers clickable (cursor pointer) with sort icon `⇅`, changing to `▲`/`▼` when sorted
|
|
252
|
+
- `<tbody>` rows: `border-bottom: 1px solid #edf2f7`, hover `#f7fafc`
|
|
253
|
+
- Cell padding: `10px 14px`
|
|
254
|
+
- Column classes:
|
|
255
|
+
- `.td-num`: color `#a0aec0`, `0.75rem`, width `44px`
|
|
256
|
+
- `.td-cat`: color `#4a5568`, `font-weight: 500`
|
|
257
|
+
- `.td-sub`: color `#718096`
|
|
258
|
+
- `.td-path`: monospace (`'SFMono-Regular', Consolas, monospace`), `0.8rem`, color `#2d3748`
|
|
259
|
+
- `.td-desc`: color `#4a5568`, `max-width: 280px`
|
|
260
|
+
- `.td-func`: monospace, `0.78rem`, color `#553c9a`
|
|
261
|
+
|
|
262
|
+
**Badges** (`.badge`):
|
|
263
|
+
- `display: inline-flex`, padding `2px 8px`, `border-radius: 4px`, `0.72rem` bold uppercase
|
|
264
|
+
- Method colors: GET (`#ebf8ff`/`#2b6cb0`), POST (`#f0fff4`/`#276749`), PATCH (`#fffff0`/`#975a16`), DELETE (`#fff5f5`/`#c53030`), PUT (`#f0e6ff`/`#553c9a`)
|
|
265
|
+
- Status colors: Implemented (`#f0fff4`/`#276749`), Missing (`#fff5f5`/`#c53030`)
|
|
266
|
+
|
|
267
|
+
**Empty state**: centered, padding `48px`, color `#a0aec0`, with a search SVG icon
|
|
268
|
+
|
|
269
|
+
**Footer**: centered, padding `24px`, color `#a0aec0`, `0.8rem`
|
|
270
|
+
|
|
271
|
+
**Columns**: #, Category, Subcategory, Method, Path, Description, Status, Toolkit Function (8 columns)
|
|
234
272
|
|
|
235
273
|
**JavaScript**:
|
|
236
274
|
- All endpoint data in a `const endpoints = [...]` array with objects: `{cat, sub, method, path, desc, status, func}`
|
|
275
|
+
- Endpoints grouped by category/subcategory with `// ─── Category - Subcategory ───` comment dividers
|
|
237
276
|
- `status` values: `"impl"` for implemented, `"miss"` for missing
|
|
238
277
|
- `func` is empty string for missing endpoints
|
|
239
|
-
- `
|
|
240
|
-
- `
|
|
241
|
-
- `
|
|
278
|
+
- `methodBadge(m)` and `statusBadge(s)` helper functions
|
|
279
|
+
- `renderTable(data)` function to populate tbody and update filter count
|
|
280
|
+
- `filterTable()` function combining all four filter inputs (status, category, method, text search across path+func+desc+sub)
|
|
281
|
+
- `sortTable(colIndex)` function with toggle direction, updating header sort icons
|
|
282
|
+
- `sortCol`/`sortAsc` state variables
|
|
283
|
+
- Initial call to `renderTable(endpoints)` at the end
|
|
242
284
|
- All code inline (no external dependencies)
|
|
243
285
|
|
|
244
286
|
## Important Guidelines
|
|
@@ -403,6 +403,27 @@ if (isCameraOnline(camera.status)) {
|
|
|
403
403
|
}
|
|
404
404
|
```
|
|
405
405
|
|
|
406
|
+
## PTZ Camera Support
|
|
407
|
+
|
|
408
|
+
When the user asks about PTZ (Pan/Tilt/Zoom) camera controls, presets, or camera movement,
|
|
409
|
+
**delegate to the `een-ptz-agent`** which has specialized knowledge of:
|
|
410
|
+
- `getPtzPosition()`, `movePtz()`, `getPtzSettings()`, `updatePtzSettings()`
|
|
411
|
+
- Direction pad controls, click-to-center, preset management
|
|
412
|
+
- PTZ automation modes (homeReturn, tour, manualOnly)
|
|
413
|
+
|
|
414
|
+
To check if a camera supports PTZ, use `getCamera(id, { include: ['capabilities'] })` and check
|
|
415
|
+
the nested `capabilities.ptz.capable` field:
|
|
416
|
+
|
|
417
|
+
```typescript
|
|
418
|
+
const result = await getCamera(cameraId, { include: ['capabilities'] })
|
|
419
|
+
const capabilities = result.data?.capabilities as { ptz?: { capable?: boolean } } | undefined
|
|
420
|
+
if (capabilities?.ptz?.capable) {
|
|
421
|
+
// This camera supports PTZ - use een-ptz-agent for PTZ-specific tasks
|
|
422
|
+
}
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
**IMPORTANT:** The capability is at `capabilities.ptz.capable` (nested object), NOT `capabilities.ptzCapable`.
|
|
426
|
+
|
|
406
427
|
## Constraints
|
|
407
428
|
- Always check authentication before API calls
|
|
408
429
|
- Use appropriate status filters to reduce payload
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: een-ptz-agent
|
|
3
|
+
description: |
|
|
4
|
+
Use this agent when working with PTZ camera controls: getting position,
|
|
5
|
+
moving cameras, managing presets, or implementing PTZ control UI
|
|
6
|
+
with the een-api-toolkit.
|
|
7
|
+
model: inherit
|
|
8
|
+
color: orange
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
You are an expert in PTZ (Pan/Tilt/Zoom) camera control with the een-api-toolkit.
|
|
12
|
+
|
|
13
|
+
## Examples
|
|
14
|
+
|
|
15
|
+
<example>
|
|
16
|
+
Context: User wants to add PTZ controls to their app.
|
|
17
|
+
user: "How do I add PTZ controls to my camera view?"
|
|
18
|
+
assistant: "I'll use the een-ptz-agent to help implement PTZ controls using movePtz() and getPtzPosition()."
|
|
19
|
+
<Task tool call to launch een-ptz-agent>
|
|
20
|
+
</example>
|
|
21
|
+
|
|
22
|
+
<example>
|
|
23
|
+
Context: User wants to manage PTZ presets.
|
|
24
|
+
user: "How do I save and load PTZ presets?"
|
|
25
|
+
assistant: "I'll use the een-ptz-agent to implement preset management with getPtzSettings() and updatePtzSettings()."
|
|
26
|
+
<Task tool call to launch een-ptz-agent>
|
|
27
|
+
</example>
|
|
28
|
+
|
|
29
|
+
<example>
|
|
30
|
+
Context: User wants click-to-center functionality.
|
|
31
|
+
user: "How do I make the camera center on where I click in the video?"
|
|
32
|
+
assistant: "I'll use the een-ptz-agent to implement click-to-center using movePtz() with the centerOn move type."
|
|
33
|
+
<Task tool call to launch een-ptz-agent>
|
|
34
|
+
</example>
|
|
35
|
+
|
|
36
|
+
## Context Files
|
|
37
|
+
- docs/AI-CONTEXT.md (overview)
|
|
38
|
+
- docs/ai-reference/AI-AUTH.md (auth is required)
|
|
39
|
+
- docs/ai-reference/AI-DEVICES.md (camera selection)
|
|
40
|
+
- docs/ai-reference/AI-PTZ.md (primary reference)
|
|
41
|
+
- docs/ai-reference/AI-MEDIA.md (live video integration)
|
|
42
|
+
|
|
43
|
+
## Reference Examples
|
|
44
|
+
- examples/vue-ptz/ (complete PTZ control app with live video)
|
|
45
|
+
|
|
46
|
+
## Your Capabilities
|
|
47
|
+
1. Get current PTZ position with getPtzPosition()
|
|
48
|
+
2. Move cameras with movePtz() (position, direction, centerOn)
|
|
49
|
+
3. Get PTZ settings and presets with getPtzSettings()
|
|
50
|
+
4. Update settings, presets, and mode with updatePtzSettings()
|
|
51
|
+
5. Implement direction pad controls
|
|
52
|
+
6. Implement click-to-center on live video
|
|
53
|
+
7. Manage PTZ presets (save, load, delete)
|
|
54
|
+
8. Configure automation modes (homeReturn, tour, manualOnly)
|
|
55
|
+
|
|
56
|
+
## Key Types
|
|
57
|
+
|
|
58
|
+
### PtzPosition
|
|
59
|
+
```typescript
|
|
60
|
+
interface PtzPosition {
|
|
61
|
+
x?: number // Pan (horizontal)
|
|
62
|
+
y?: number // Tilt (vertical)
|
|
63
|
+
z?: number // Zoom level
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### PtzMove (discriminated union)
|
|
68
|
+
```typescript
|
|
69
|
+
// Absolute position
|
|
70
|
+
{ moveType: 'position', x?: number, y?: number, z?: number }
|
|
71
|
+
|
|
72
|
+
// Relative direction
|
|
73
|
+
{ moveType: 'direction', direction: PtzDirection[], stepSize?: PtzStepSize }
|
|
74
|
+
|
|
75
|
+
// Center on point in frame
|
|
76
|
+
{ moveType: 'centerOn', relativeX: number, relativeY: number }
|
|
77
|
+
|
|
78
|
+
type PtzDirection = 'up' | 'down' | 'left' | 'right' | 'in' | 'out'
|
|
79
|
+
type PtzStepSize = 'small' | 'medium' | 'large'
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### PtzSettings
|
|
83
|
+
```typescript
|
|
84
|
+
interface PtzSettings {
|
|
85
|
+
presets: PtzPreset[]
|
|
86
|
+
homePreset: string | null
|
|
87
|
+
mode: PtzMode // 'homeReturn' | 'tour' | 'manualOnly'
|
|
88
|
+
autoStartDelay: number // seconds
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
interface PtzPreset {
|
|
92
|
+
name: string
|
|
93
|
+
position: PtzPosition
|
|
94
|
+
timeAtPreset: number // seconds at preset during tour
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### PtzSettingsUpdate
|
|
99
|
+
```typescript
|
|
100
|
+
interface PtzSettingsUpdate {
|
|
101
|
+
presets?: PtzPreset[]
|
|
102
|
+
homePreset?: string | null
|
|
103
|
+
mode?: PtzMode
|
|
104
|
+
autoStartDelay?: number
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Key Functions
|
|
109
|
+
|
|
110
|
+
### getPtzPosition(cameraId)
|
|
111
|
+
Get current camera position:
|
|
112
|
+
```typescript
|
|
113
|
+
import { getPtzPosition } from 'een-api-toolkit'
|
|
114
|
+
|
|
115
|
+
const { data, error } = await getPtzPosition('camera-123')
|
|
116
|
+
if (data) {
|
|
117
|
+
console.log(`Pan: ${data.x}, Tilt: ${data.y}, Zoom: ${data.z}`)
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### movePtz(cameraId, move)
|
|
122
|
+
Move camera with three move types:
|
|
123
|
+
```typescript
|
|
124
|
+
import { movePtz } from 'een-api-toolkit'
|
|
125
|
+
|
|
126
|
+
// Absolute position
|
|
127
|
+
await movePtz('camera-123', { moveType: 'position', x: 0.5, y: -0.3, z: 2.0 })
|
|
128
|
+
|
|
129
|
+
// Direction with step size
|
|
130
|
+
await movePtz('camera-123', {
|
|
131
|
+
moveType: 'direction',
|
|
132
|
+
direction: ['up', 'left'],
|
|
133
|
+
stepSize: 'medium'
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
// Center on point in video frame (0.0 to 1.0)
|
|
137
|
+
await movePtz('camera-123', {
|
|
138
|
+
moveType: 'centerOn',
|
|
139
|
+
relativeX: 0.75,
|
|
140
|
+
relativeY: 0.5
|
|
141
|
+
})
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### getPtzSettings(cameraId)
|
|
145
|
+
Get presets and automation settings:
|
|
146
|
+
```typescript
|
|
147
|
+
import { getPtzSettings } from 'een-api-toolkit'
|
|
148
|
+
|
|
149
|
+
const { data, error } = await getPtzSettings('camera-123')
|
|
150
|
+
if (data) {
|
|
151
|
+
console.log('Mode:', data.mode)
|
|
152
|
+
console.log('Presets:', data.presets.map(p => p.name))
|
|
153
|
+
console.log('Home:', data.homePreset)
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### updatePtzSettings(cameraId, settings)
|
|
158
|
+
Update settings (partial update - only provided fields change):
|
|
159
|
+
```typescript
|
|
160
|
+
import { updatePtzSettings } from 'een-api-toolkit'
|
|
161
|
+
|
|
162
|
+
// Change mode
|
|
163
|
+
await updatePtzSettings('camera-123', { mode: 'tour' })
|
|
164
|
+
|
|
165
|
+
// To add a preset, first fetch existing presets and append the new one
|
|
166
|
+
const { data: settings } = await getPtzSettings('camera-123')
|
|
167
|
+
const newPreset = { name: 'Entrance', position: { x: 0, y: 0, z: 1 }, timeAtPreset: 10 }
|
|
168
|
+
await updatePtzSettings('camera-123', {
|
|
169
|
+
presets: [...(settings?.presets ?? []), newPreset],
|
|
170
|
+
homePreset: 'Entrance'
|
|
171
|
+
})
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Click-to-Center Pattern
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
function handleVideoClick(event: MouseEvent) {
|
|
178
|
+
const video = event.currentTarget as HTMLVideoElement
|
|
179
|
+
const rect = video.getBoundingClientRect()
|
|
180
|
+
const relativeX = (event.clientX - rect.left) / rect.width
|
|
181
|
+
const relativeY = (event.clientY - rect.top) / rect.height
|
|
182
|
+
|
|
183
|
+
movePtz(cameraId, {
|
|
184
|
+
moveType: 'centerOn',
|
|
185
|
+
relativeX,
|
|
186
|
+
relativeY
|
|
187
|
+
})
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Error Handling
|
|
192
|
+
|
|
193
|
+
| Error Code | Meaning | Action |
|
|
194
|
+
|------------|---------|--------|
|
|
195
|
+
| AUTH_REQUIRED | Not authenticated | Redirect to login |
|
|
196
|
+
| NOT_FOUND | Camera not found or no PTZ support | Show "not found" message |
|
|
197
|
+
| FORBIDDEN | No permission | Show access denied message |
|
|
198
|
+
| VALIDATION_ERROR | Empty camera ID | Fix input |
|
|
199
|
+
| API_ERROR | Server error | Show error, allow retry |
|
|
200
|
+
|
|
201
|
+
## Detecting PTZ-Capable Cameras
|
|
202
|
+
|
|
203
|
+
To check if a camera supports PTZ, fetch it with `include: ['capabilities']` and check the
|
|
204
|
+
nested `capabilities.ptz.capable` field. The structure is:
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
// Response from getCamera(id, { include: ['capabilities'] })
|
|
208
|
+
{
|
|
209
|
+
capabilities: {
|
|
210
|
+
ptz: {
|
|
211
|
+
capable: true, // Camera supports PTZ
|
|
212
|
+
panTilt: true, // Supports pan/tilt
|
|
213
|
+
zoom: true, // Supports zoom
|
|
214
|
+
positionMove: true, // Supports absolute position moves
|
|
215
|
+
directionMove: true, // Supports directional moves
|
|
216
|
+
centerOnMove: true, // Supports center-on moves
|
|
217
|
+
fisheye: false // Whether camera is fisheye
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**IMPORTANT:** The PTZ capability is at `capabilities.ptz.capable` (nested under a `ptz` object),
|
|
224
|
+
NOT at `capabilities.ptzCapable` (flat). Fisheye cameras report `capabilities.ptz.capable: true`
|
|
225
|
+
but are NOT true PTZ cameras — always exclude them. Use this pattern:
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
import { computed } from 'vue'
|
|
229
|
+
|
|
230
|
+
const isPtzCapable = computed(() => {
|
|
231
|
+
const ptz = camera.value?.capabilities?.ptz
|
|
232
|
+
return ptz?.capable === true && ptz?.fisheye !== true
|
|
233
|
+
})
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Also check `effectivePermissions.controlPTZ` to verify the user has permission to move the camera,
|
|
237
|
+
and `effectivePermissions.editPTZStations` for managing presets.
|
|
238
|
+
|
|
239
|
+
## Constraints
|
|
240
|
+
- Always check authentication before API calls
|
|
241
|
+
- Verify camera has PTZ capability (`capabilities.ptz.capable`) and is not fisheye (`capabilities.ptz.fisheye !== true`) before showing controls
|
|
242
|
+
- Check user permissions (`effectivePermissions.controlPTZ`) before enabling movement
|
|
243
|
+
- Poll position periodically (every 5s) for position display
|
|
244
|
+
- Handle 204 responses for PUT/PATCH (no response body)
|
|
245
|
+
- Use encodeURIComponent for camera IDs in URLs
|
package/CHANGELOG.md
CHANGED
|
@@ -2,72 +2,92 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
-
## [0.3.
|
|
5
|
+
## [0.3.97] - 2026-02-22
|
|
6
6
|
|
|
7
7
|
### Release Summary
|
|
8
8
|
|
|
9
|
-
#### PR #
|
|
9
|
+
#### PR #128: Release v0.3.89: PTZ camera control API and vue-ptz example
|
|
10
10
|
## Summary
|
|
11
|
-
- **HIGH**: Validate hostname before persisting OAuth token — `setBaseUrl()` now returns boolean; `handleAuthCallback()` checks it before calling `setToken()`, preventing token persistence when hostname is rejected
|
|
12
|
-
- **MEDIUM**: Pin external proxy repo clone in CI workflows (`validate-pr`, `test-release`) to commit SHA `4e8f45f` to prevent supply chain attacks
|
|
13
|
-
- **LOW**: Remove unnecessary `id-token: write` permission from `claude-code-review` workflow
|
|
14
11
|
|
|
15
|
-
|
|
16
|
-
- [x] 644/644 unit tests pass
|
|
17
|
-
- [x] 225/225 E2E tests pass across all 11 example apps
|
|
18
|
-
- [x] Lint clean, build succeeds
|
|
19
|
-
- [ ] Validate-PR workflow passes on this PR
|
|
12
|
+
This release adds PTZ (Pan/Tilt/Zoom) camera control support to the toolkit:
|
|
20
13
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
14
|
+
- **PTZ API functions**: `getPtzPosition()`, `movePtz()`, `getPtzSettings()`, `updatePtzSettings()` with full TypeScript types
|
|
15
|
+
- **PTZ types**: `PtzPosition`, `PtzMove` (discriminated union: position/direction/centerOn), `PtzSettings`, `PtzPreset`, `PtzSettingsUpdate`
|
|
16
|
+
- **vue-ptz example app**: Complete PTZ control application with live video, direction pad, click-to-center, position display, preset management, and API call logging
|
|
17
|
+
- **Camera capabilities**: Added `capabilities` field to `Camera` type for PTZ detection via `include: ['capabilities']`
|
|
18
|
+
- **Documentation**: AI-PTZ.md reference doc, een-ptz-agent, updated AI-CONTEXT.md and CLAUDE.md
|
|
19
|
+
- **E2E tests**: Conditional PTZ API tests that exercise position read, direction move, and preset loading when a PTZ camera is available
|
|
25
20
|
|
|
26
|
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
- Fix workflow_dispatch restriction to production branch (security)
|
|
21
|
+
### Additional changes
|
|
22
|
+
- Dependabot: CodeQL action bump to 4.32.3
|
|
23
|
+
- CI: Use floating v1 tag for claude-code-action instead of SHA pin
|
|
30
24
|
|
|
31
25
|
## Commits
|
|
32
26
|
|
|
33
|
-
- `
|
|
34
|
-
- `
|
|
35
|
-
- `
|
|
36
|
-
- `
|
|
37
|
-
- `
|
|
27
|
+
- `c552fd0` feat: add PTZ camera control API and vue-ptz example app
|
|
28
|
+
- `6a12649` fix: use single getCameras call with include and pagination for PTZ discovery
|
|
29
|
+
- `2b04b26` fix: address review findings in vue-ptz example app
|
|
30
|
+
- `92a18e4` fix: address remaining review findings
|
|
31
|
+
- `4d01c2c` fix: use floating v1 tag for claude-code-action instead of SHA pin
|
|
32
|
+
- `3f197ba` chore(deps): bump github/codeql-action from 4.32.2 to 4.32.3
|
|
33
|
+
|
|
34
|
+
## Test Results
|
|
35
|
+
|
|
36
|
+
| Check | Result |
|
|
37
|
+
|-------|--------|
|
|
38
|
+
| Lint | Pass (0 errors) |
|
|
39
|
+
| Unit tests | 681 passed (24 suites) |
|
|
40
|
+
| Build | Pass |
|
|
41
|
+
| E2E (12 apps) | All passed |
|
|
42
|
+
| Security review | No vulnerabilities found |
|
|
43
|
+
| Confidential data scan | Clean |
|
|
38
44
|
|
|
39
|
-
|
|
45
|
+
**Version**: 0.3.89
|
|
40
46
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
+
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
|
48
|
+
|
|
49
|
+
#### PR #130: Release v0.3.96: PTZ fisheye exclusion and API coverage docs
|
|
50
|
+
## Summary
|
|
51
|
+
- Add fisheye camera exclusion to PTZ type definitions, agent docs, AI reference docs, and vue-ptz example
|
|
52
|
+
- Add PTZ sub-capability fields (`fisheye`, `panTilt`, `zoom`, `positionMove`, `directionMove`, `centerOnMove`) to `Camera.capabilities.ptz` type
|
|
53
|
+
- Regenerate API coverage docs with full-width layout and scrollbar fix
|
|
47
54
|
|
|
48
55
|
## Version
|
|
56
|
+
v0.3.96
|
|
49
57
|
|
|
50
|
-
|
|
58
|
+
## Commits
|
|
59
|
+
- `1d9da62` docs: regenerate API coverage docs with full-width layout and scrollbar fix
|
|
60
|
+
- `044c9ae` docs: add fisheye camera exclusion guidance to PTZ agent
|
|
61
|
+
- `f65ff2e` fix: add PTZ sub-capability fields to Camera type and update docs
|
|
62
|
+
- `6ecf253` fix: address review findings for PTZ fisheye guidance
|
|
63
|
+
- `97cf632` fix: exclude fisheye cameras from PTZ selector in vue-ptz example
|
|
64
|
+
|
|
65
|
+
## Test Results
|
|
66
|
+
- Lint: passed (1 pre-existing warning)
|
|
67
|
+
- Unit tests: 683/683 passed
|
|
68
|
+
- Build: passed
|
|
69
|
+
- E2E: 12/12 example apps passed
|
|
70
|
+
- Security: no vulnerabilities (type/docs changes only)
|
|
71
|
+
- Confidential data scan: clean across 280 changed .md files
|
|
51
72
|
|
|
52
73
|
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
|
53
74
|
|
|
54
75
|
|
|
55
76
|
### Detailed Changes
|
|
56
77
|
|
|
57
|
-
#### Features
|
|
58
|
-
- feat: add EEN API coverage documentation and agent
|
|
59
|
-
|
|
60
78
|
#### Bug Fixes
|
|
61
|
-
- fix:
|
|
62
|
-
- fix:
|
|
79
|
+
- fix: fix sorting bug in coverage table and add undefined fisheye test
|
|
80
|
+
- fix: exclude fisheye cameras from PTZ selector in vue-ptz example
|
|
81
|
+
- fix: address review findings for PTZ fisheye guidance
|
|
82
|
+
- fix: add PTZ sub-capability fields to Camera type and update docs
|
|
63
83
|
|
|
64
84
|
#### Other Changes
|
|
65
|
-
- docs:
|
|
66
|
-
- docs:
|
|
85
|
+
- docs: add fisheye camera exclusion guidance to PTZ agent
|
|
86
|
+
- docs: regenerate API coverage docs with full-width layout and scrollbar fix
|
|
67
87
|
|
|
68
88
|
### Links
|
|
69
89
|
- [npm package](https://www.npmjs.com/package/een-api-toolkit)
|
|
70
|
-
- [Full Changelog](https://github.com/klaushofrichter/een-api-toolkit/compare/v0.3.
|
|
90
|
+
- [Full Changelog](https://github.com/klaushofrichter/een-api-toolkit/compare/v0.3.91...v0.3.97)
|
|
71
91
|
|
|
72
92
|
---
|
|
73
|
-
*Released: 2026-02-
|
|
93
|
+
*Released: 2026-02-22 19:53:20 CST*
|