een-api-toolkit 0.3.97 → 0.3.101

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/CHANGELOG.md CHANGED
@@ -2,50 +2,10 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
- ## [0.3.97] - 2026-02-22
5
+ ## [0.3.101] - 2026-02-23
6
6
 
7
7
  ### Release Summary
8
8
 
9
- #### PR #128: Release v0.3.89: PTZ camera control API and vue-ptz example
10
- ## Summary
11
-
12
- This release adds PTZ (Pan/Tilt/Zoom) camera control support to the toolkit:
13
-
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
20
-
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
24
-
25
- ## Commits
26
-
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 |
44
-
45
- **Version**: 0.3.89
46
-
47
- 🤖 Generated with [Claude Code](https://claude.com/claude-code)
48
-
49
9
  #### PR #130: Release v0.3.96: PTZ fisheye exclusion and API coverage docs
50
10
  ## Summary
51
11
  - Add fisheye camera exclusion to PTZ type definitions, agent docs, AI reference docs, and vue-ptz example
@@ -72,22 +32,53 @@ v0.3.96
72
32
 
73
33
  🤖 Generated with [Claude Code](https://claude.com/claude-code)
74
34
 
35
+ #### PR #134: release: v0.3.101 - multi-move-type PTZ support
36
+ ## Summary
37
+ - Add move type dropdown (Position / Direction / Center On) to the PositionInput panel in `examples/vue-ptz`
38
+ - Dynamic input fields per move type with proper validation
39
+ - Replace auto-updating x/y/z with explicit "Import Current Position" button
40
+ - Refactored `apply()` with `buildMove()` extraction for cleaner code
41
+ - `try/finally` for defensive state reset, null-safe optional chaining
42
+
43
+ ## Version
44
+ `0.3.101`
45
+
46
+ ## Commits
47
+ - `2a4aac8f` feat: add multi-move-type support to vue-ptz PositionInput
48
+ - `f125139c` fix: address code review findings for PositionInput
49
+ - `addcc0ec` fix: wrap movePtz calls in try/finally to always reset applying state
50
+ - `a1365f24` fix: address remaining review findings for PositionInput
51
+
52
+ ## Local test results
53
+ - Lint: passed (0 errors, 1 pre-existing warning)
54
+ - Unit tests: 684/684 passed
55
+ - Build: success
56
+ - E2E tests: 12/12 example apps passed
57
+ - Security review: no issues found
58
+ - Docs confidential data scan: clean (274 .md files scanned)
59
+
60
+ ## Test plan
61
+ - [ ] Select "Position" move type — verify x/y/z inputs, Import Current Position button
62
+ - [ ] Select "Direction" move type — verify checkboxes + step size, Apply sends direction move
63
+ - [ ] Select "Center On" move type — verify rX/rY inputs, Apply sends centerOn move
64
+ - [ ] Verify x/y/z fields do NOT auto-update when position refreshes
65
+
66
+ 🤖 Generated with [Claude Code](https://claude.com/claude-code)
67
+
75
68
 
76
69
  ### Detailed Changes
77
70
 
78
- #### Bug Fixes
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
71
+ #### Features
72
+ - feat: add multi-move-type support to vue-ptz PositionInput
83
73
 
84
- #### Other Changes
85
- - docs: add fisheye camera exclusion guidance to PTZ agent
86
- - docs: regenerate API coverage docs with full-width layout and scrollbar fix
74
+ #### Bug Fixes
75
+ - fix: address remaining review findings for PositionInput
76
+ - fix: wrap movePtz calls in try/finally to always reset applying state
77
+ - fix: address code review findings for PositionInput
87
78
 
88
79
  ### Links
89
80
  - [npm package](https://www.npmjs.com/package/een-api-toolkit)
90
- - [Full Changelog](https://github.com/klaushofrichter/een-api-toolkit/compare/v0.3.91...v0.3.97)
81
+ - [Full Changelog](https://github.com/klaushofrichter/een-api-toolkit/compare/v0.3.97...v0.3.101)
91
82
 
92
83
  ---
93
- *Released: 2026-02-22 19:53:20 CST*
84
+ *Released: 2026-02-23 18:17:46 CST*
@@ -1,6 +1,6 @@
1
1
  # EEN API Toolkit - AI Reference
2
2
 
3
- > **Version:** 0.3.97
3
+ > **Version:** 0.3.101
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.
@@ -1,6 +1,6 @@
1
1
  # Authentication - EEN API Toolkit
2
2
 
3
- > **Version:** 0.3.97
3
+ > **Version:** 0.3.101
4
4
  >
5
5
  > OAuth flow implementation, token management, and session handling.
6
6
  > Load this document when implementing login, logout, or auth guards.
@@ -1,6 +1,6 @@
1
1
  # Automations API - EEN API Toolkit
2
2
 
3
- > **Version:** 0.3.97
3
+ > **Version:** 0.3.101
4
4
  >
5
5
  > Complete reference for automation rules and alert actions.
6
6
  > Load this document when working with automated alert workflows.
@@ -1,6 +1,6 @@
1
1
  # Cameras & Bridges API - EEN API Toolkit
2
2
 
3
- > **Version:** 0.3.97
3
+ > **Version:** 0.3.101
4
4
  >
5
5
  > Complete reference for camera and bridge management.
6
6
  > Load this document when working with devices.
@@ -1,6 +1,6 @@
1
1
  # Event Type to Data Schemas Mapping - EEN API Toolkit
2
2
 
3
- > **Version:** 0.3.97
3
+ > **Version:** 0.3.101
4
4
  >
5
5
  > Complete reference for event type to data schema mappings.
6
6
  > Load this document when building dynamic event queries with the `include` parameter.
@@ -1,6 +1,6 @@
1
1
  # Events, Alerts & Real-Time Streaming - EEN API Toolkit
2
2
 
3
- > **Version:** 0.3.97
3
+ > **Version:** 0.3.101
4
4
  >
5
5
  > Complete reference for events, alerts, metrics, and SSE subscriptions.
6
6
  > Load this document when implementing event-driven features.
@@ -1,6 +1,6 @@
1
1
  # Layouts API - EEN API Toolkit
2
2
 
3
- > **Version:** 0.3.97
3
+ > **Version:** 0.3.101
4
4
  >
5
5
  > Complete reference for layout management (camera grouping).
6
6
  > Load this document when working with layouts.
@@ -1,6 +1,6 @@
1
1
  # Jobs, Exports, Files & Downloads - EEN API Toolkit
2
2
 
3
- > **Version:** 0.3.97
3
+ > **Version:** 0.3.101
4
4
  >
5
5
  > Complete reference for async jobs, video exports, and file management.
6
6
  > Load this document when implementing export workflows or file downloads.
@@ -1,6 +1,6 @@
1
1
  # Media & Live Video - EEN API Toolkit
2
2
 
3
- > **Version:** 0.3.97
3
+ > **Version:** 0.3.101
4
4
  >
5
5
  > Complete reference for media retrieval, live streaming, and video playback.
6
6
  > Load this document when implementing video features.
@@ -1,6 +1,6 @@
1
1
  # PTZ Camera Controls
2
2
 
3
- > **Version:** 0.3.97
3
+ > **Version:** 0.3.101
4
4
  >
5
5
  > Pan/Tilt/Zoom camera control: position, movement, presets, and automation.
6
6
 
@@ -1,6 +1,6 @@
1
1
  # Vue 3 Application Setup - EEN API Toolkit
2
2
 
3
- > **Version:** 0.3.97
3
+ > **Version:** 0.3.101
4
4
  >
5
5
  > Complete guide for setting up a Vue 3 application with the een-api-toolkit.
6
6
  > Load this document when creating a new project or troubleshooting setup issues.
@@ -1,6 +1,6 @@
1
1
  # Users API - EEN API Toolkit
2
2
 
3
- > **Version:** 0.3.97
3
+ > **Version:** 0.3.101
4
4
  >
5
5
  > Complete reference for user management.
6
6
  > Load this document when working with user data.
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { ref, watch } from 'vue'
3
3
  import { movePtz } from 'een-api-toolkit'
4
- import type { PtzPositionResponse } from 'een-api-toolkit'
4
+ import type { PtzPositionResponse, PtzDirection, PtzStepSize, PtzMoveType, PtzMove } from 'een-api-toolkit'
5
5
  import { useApiLog } from '../composables/useApiLog'
6
6
 
7
7
  const props = defineProps<{
@@ -14,45 +14,97 @@ const emit = defineEmits<{
14
14
  }>()
15
15
 
16
16
  const { log: apiLog } = useApiLog()
17
+
18
+ // Move type selection
19
+ const moveType = ref<PtzMoveType>('position')
20
+
21
+ // Position fields
17
22
  const x = ref<string>('0')
18
23
  const y = ref<string>('0')
19
24
  const z = ref<string>('0')
25
+
26
+ // Direction fields
27
+ const directions = ref<PtzDirection[]>([])
28
+ const stepSize = ref<PtzStepSize>('medium')
29
+ const allDirections: PtzDirection[] = ['up', 'down', 'left', 'right', 'in', 'out']
30
+
31
+ // CenterOn fields
32
+ const relativeX = ref<string>('0.5')
33
+ const relativeY = ref<string>('0.5')
34
+
20
35
  const applying = ref(false)
21
36
  const error = ref<string | null>(null)
22
37
 
23
- watch(() => props.currentPosition, (pos) => {
24
- if (pos) {
25
- x.value = pos.x?.toFixed(3) ?? '0'
26
- y.value = pos.y?.toFixed(3) ?? '0'
27
- z.value = pos.z?.toFixed(3) ?? '0'
38
+ watch(moveType, () => {
39
+ error.value = null
40
+ })
41
+
42
+ function importPosition() {
43
+ if (props.currentPosition) {
44
+ x.value = props.currentPosition.x?.toFixed(3) ?? '0'
45
+ y.value = props.currentPosition.y?.toFixed(3) ?? '0'
46
+ z.value = props.currentPosition.z?.toFixed(3) ?? '0'
28
47
  }
29
- }, { immediate: true })
48
+ }
30
49
 
31
- async function apply() {
32
- if (!props.cameraId || applying.value) return
50
+ function buildMove(): PtzMove | null {
51
+ if (moveType.value === 'position') {
52
+ const xVal = parseFloat(x.value)
53
+ const yVal = parseFloat(y.value)
54
+ const zVal = parseFloat(z.value)
55
+
56
+ if (isNaN(xVal) || isNaN(yVal) || isNaN(zVal)) {
57
+ error.value = 'Invalid numeric values'
58
+ return null
59
+ }
60
+
61
+ // Position coordinates are camera-specific absolute values (not normalised to 0-1)
62
+ return { moveType: 'position', x: xVal, y: yVal, z: zVal }
63
+ } else if (moveType.value === 'direction') {
64
+ if (directions.value.length === 0) {
65
+ error.value = 'Select at least one direction'
66
+ return null
67
+ }
33
68
 
34
- const xVal = parseFloat(x.value)
35
- const yVal = parseFloat(y.value)
36
- const zVal = parseFloat(z.value)
69
+ return { moveType: 'direction', direction: [...directions.value], stepSize: stepSize.value }
70
+ } else {
71
+ const rxVal = parseFloat(relativeX.value)
72
+ const ryVal = parseFloat(relativeY.value)
73
+
74
+ if (isNaN(rxVal) || isNaN(ryVal)) {
75
+ error.value = 'Invalid numeric values'
76
+ return null
77
+ }
37
78
 
38
- if (isNaN(xVal) || isNaN(yVal) || isNaN(zVal)) {
39
- error.value = 'Invalid numeric values'
40
- return
79
+ if (rxVal < 0 || rxVal > 1 || ryVal < 0 || ryVal > 1) {
80
+ error.value = 'Values must be between 0 and 1'
81
+ return null
82
+ }
83
+
84
+ return { moveType: 'centerOn', relativeX: rxVal, relativeY: ryVal }
41
85
  }
86
+ }
87
+
88
+ async function apply() {
89
+ if (!props.cameraId || applying.value) return
42
90
 
43
- applying.value = true
44
91
  error.value = null
45
92
 
46
- const move = { moveType: 'position' as const, x: xVal, y: yVal, z: zVal }
47
- const result = await movePtz(props.cameraId, move)
48
- apiLog('movePtz', { cameraId: props.cameraId, move }, result.error ?? result.data, !!result.error)
93
+ const move = buildMove()
94
+ if (!move) return
49
95
 
50
- applying.value = false
96
+ applying.value = true
97
+ try {
98
+ const result = await movePtz(props.cameraId, move)
99
+ apiLog('movePtz', { cameraId: props.cameraId, move }, result.error ?? result.data, !!result.error)
51
100
 
52
- if (result.error) {
53
- error.value = result.error.message
54
- } else {
55
- emit('move-complete')
101
+ if (result.error) {
102
+ error.value = result.error.message
103
+ } else {
104
+ emit('move-complete')
105
+ }
106
+ } finally {
107
+ applying.value = false
56
108
  }
57
109
  }
58
110
  </script>
@@ -60,39 +112,114 @@ async function apply() {
60
112
  <template>
61
113
  <div class="position-input" data-testid="position-input">
62
114
  <div class="input-row">
63
- <label>X</label>
64
- <input
65
- v-model="x"
66
- type="number"
67
- step="0.01"
68
- :disabled="!cameraId || applying"
69
- data-testid="input-x"
70
- @keyup.enter="apply"
71
- />
72
- </div>
73
- <div class="input-row">
74
- <label>Y</label>
75
- <input
76
- v-model="y"
77
- type="number"
78
- step="0.01"
79
- :disabled="!cameraId || applying"
80
- data-testid="input-y"
81
- @keyup.enter="apply"
82
- />
83
- </div>
84
- <div class="input-row">
85
- <label>Z</label>
86
- <input
87
- v-model="z"
88
- type="number"
89
- step="0.1"
90
- min="0"
91
- :disabled="!cameraId || applying"
92
- data-testid="input-z"
93
- @keyup.enter="apply"
94
- />
115
+ <label>Move</label>
116
+ <select v-model="moveType" :disabled="!cameraId || applying" data-testid="move-type-select">
117
+ <option value="position">Position</option>
118
+ <option value="direction">Direction</option>
119
+ <option value="centerOn">Center On</option>
120
+ </select>
95
121
  </div>
122
+
123
+ <!-- Position fields -->
124
+ <template v-if="moveType === 'position'">
125
+ <div class="input-row">
126
+ <label>X</label>
127
+ <input
128
+ v-model="x"
129
+ type="number"
130
+ step="0.01"
131
+ :disabled="!cameraId || applying"
132
+ data-testid="input-x"
133
+ @keyup.enter="apply"
134
+ />
135
+ </div>
136
+ <div class="input-row">
137
+ <label>Y</label>
138
+ <input
139
+ v-model="y"
140
+ type="number"
141
+ step="0.01"
142
+ :disabled="!cameraId || applying"
143
+ data-testid="input-y"
144
+ @keyup.enter="apply"
145
+ />
146
+ </div>
147
+ <div class="input-row">
148
+ <label>Z</label>
149
+ <input
150
+ v-model="z"
151
+ type="number"
152
+ step="0.1"
153
+ min="0"
154
+ :disabled="!cameraId || applying"
155
+ data-testid="input-z"
156
+ @keyup.enter="apply"
157
+ />
158
+ </div>
159
+ <button
160
+ class="import-btn"
161
+ :disabled="!cameraId || !currentPosition || applying"
162
+ @click="importPosition"
163
+ data-testid="import-position"
164
+ >
165
+ Import Current Position
166
+ </button>
167
+ </template>
168
+
169
+ <!-- Direction fields -->
170
+ <template v-else-if="moveType === 'direction'">
171
+ <div class="direction-checkboxes">
172
+ <label v-for="dir in allDirections" :key="dir" class="checkbox-label">
173
+ <input
174
+ type="checkbox"
175
+ :value="dir"
176
+ v-model="directions"
177
+ :disabled="!cameraId || applying"
178
+ :data-testid="'dir-' + dir"
179
+ />
180
+ {{ dir }}
181
+ </label>
182
+ </div>
183
+ <div class="input-row">
184
+ <label>Step</label>
185
+ <select v-model="stepSize" :disabled="!cameraId || applying" data-testid="step-size-select">
186
+ <option value="small">Small</option>
187
+ <option value="medium">Medium</option>
188
+ <option value="large">Large</option>
189
+ </select>
190
+ </div>
191
+ </template>
192
+
193
+ <!-- CenterOn fields -->
194
+ <template v-else>
195
+ <div class="input-row">
196
+ <label>rX</label>
197
+ <input
198
+ v-model="relativeX"
199
+ type="number"
200
+ step="0.01"
201
+ min="0"
202
+ max="1"
203
+ :disabled="!cameraId || applying"
204
+ data-testid="input-relative-x"
205
+ @keyup.enter="apply"
206
+ />
207
+ </div>
208
+ <div class="input-row">
209
+ <label>rY</label>
210
+ <input
211
+ v-model="relativeY"
212
+ type="number"
213
+ step="0.01"
214
+ min="0"
215
+ max="1"
216
+ :disabled="!cameraId || applying"
217
+ data-testid="input-relative-y"
218
+ @keyup.enter="apply"
219
+ />
220
+ </div>
221
+ </template>
222
+
96
223
  <button
97
224
  class="apply-btn"
98
225
  :disabled="!cameraId || applying"
@@ -127,7 +254,7 @@ async function apply() {
127
254
  font-size: 13px;
128
255
  font-weight: 600;
129
256
  color: #555;
130
- min-width: 20px;
257
+ min-width: 32px;
131
258
  text-align: right;
132
259
  }
133
260
 
@@ -140,10 +267,61 @@ async function apply() {
140
267
  font-family: monospace;
141
268
  }
142
269
 
143
- .input-row input:disabled {
270
+ .input-row select {
271
+ flex: 1;
272
+ padding: 5px 8px;
273
+ border: 1px solid #ddd;
274
+ border-radius: 4px;
275
+ font-size: 13px;
276
+ }
277
+
278
+ .input-row input:disabled,
279
+ .input-row select:disabled {
144
280
  opacity: 0.5;
145
281
  }
146
282
 
283
+ .direction-checkboxes {
284
+ display: grid;
285
+ grid-template-columns: 1fr 1fr;
286
+ gap: 4px 12px;
287
+ margin-bottom: 8px;
288
+ }
289
+
290
+ .checkbox-label {
291
+ display: flex;
292
+ align-items: center;
293
+ gap: 6px;
294
+ font-size: 13px;
295
+ color: #555;
296
+ cursor: pointer;
297
+ text-transform: capitalize;
298
+ }
299
+
300
+ .checkbox-label input[type="checkbox"] {
301
+ margin: 0;
302
+ }
303
+
304
+ .import-btn {
305
+ width: 100%;
306
+ padding: 6px;
307
+ font-size: 11px;
308
+ background: #6c757d;
309
+ color: white;
310
+ border: none;
311
+ border-radius: 4px;
312
+ cursor: pointer;
313
+ margin-bottom: 4px;
314
+ }
315
+
316
+ .import-btn:hover:not(:disabled) {
317
+ background: #5a6268;
318
+ }
319
+
320
+ .import-btn:disabled {
321
+ opacity: 0.4;
322
+ cursor: not-allowed;
323
+ }
324
+
147
325
  .apply-btn {
148
326
  width: 100%;
149
327
  padding: 8px;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "een-api-toolkit",
3
- "version": "0.3.97",
3
+ "version": "0.3.101",
4
4
  "description": "EEN Video platform API v3.0 library for Vue 3",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",