een-api-toolkit 0.3.22 → 0.3.30

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.
@@ -71,8 +71,15 @@ type CameraStatus =
71
71
  | 'offline'
72
72
  | 'streaming'
73
73
  | 'recording'
74
+ | 'registered'
75
+ | 'deviceOffline'
76
+ | 'bridgeOffline'
77
+ | 'invalidCredentials'
74
78
  | 'error'
75
79
  | 'unknown'
80
+
81
+ // Status can also be nested in an object:
82
+ // camera.status?.connectionStatus
76
83
  ```
77
84
 
78
85
  ### Bridge Interface
@@ -112,15 +119,28 @@ interface ListCamerasParams {
112
119
  ## Key Functions
113
120
 
114
121
  ### getCameras()
115
- List cameras with optional filters:
122
+ List cameras with optional filters.
123
+
124
+ **IMPORTANT:** The `status` field is NOT included by default. You must use `include: ['status']` to receive it:
125
+
116
126
  ```typescript
117
127
  import { getCameras, type Camera, type ListCamerasParams } from 'een-api-toolkit'
118
128
 
119
129
  const cameras = ref<Camera[]>([])
120
130
 
121
- // Get all online cameras
131
+ // Get all cameras WITH status - include: ['status'] is required!
132
+ async function fetchCameras() {
133
+ const result = await getCameras({
134
+ include: ['status'], // Required to get camera.status
135
+ pageSize: 100
136
+ })
137
+ // Now camera.status will be populated
138
+ }
139
+
140
+ // Get all online cameras (still need include for display)
122
141
  async function fetchOnlineCameras() {
123
142
  const result = await getCameras({
143
+ include: ['status'], // Required to display status in UI
124
144
  status__in: ['online', 'streaming', 'recording'],
125
145
  pageSize: 100
126
146
  })
@@ -224,17 +244,33 @@ async function fetchBridge(bridgeId: string) {
224
244
 
225
245
  ```vue
226
246
  <script setup lang="ts">
227
- import { ref, onMounted } from 'vue'
228
- import { getCameras, type Camera, type ListCamerasParams } from 'een-api-toolkit'
247
+ import { ref, onMounted, computed } from 'vue'
248
+ import { getCameras, type Camera, type CameraStatus, type ListCamerasParams } from 'een-api-toolkit'
229
249
 
230
250
  const cameras = ref<Camera[]>([])
231
251
  const loading = ref(false)
232
252
  const statusFilter = ref<string[]>(['online', 'streaming', 'recording'])
233
253
 
254
+ // Helper: status can be a string OR an object with connectionStatus
255
+ function getStatusString(status?: CameraStatus | { connectionStatus?: CameraStatus }): string | undefined {
256
+ if (!status) return undefined
257
+ if (typeof status === 'string') return status
258
+ return status.connectionStatus
259
+ }
260
+
261
+ // Computed property to pre-process cameras with status string (avoids calling helper multiple times in template)
262
+ const camerasWithStatus = computed(() =>
263
+ cameras.value.map(camera => ({
264
+ ...camera,
265
+ statusString: getStatusString(camera.status),
266
+ }))
267
+ )
268
+
234
269
  async function fetchCameras() {
235
270
  loading.value = true
236
271
 
237
272
  const params: ListCamerasParams = {
273
+ include: ['status'], // Required to receive status field
238
274
  pageSize: 100
239
275
  }
240
276
 
@@ -268,10 +304,13 @@ onMounted(fetchCameras)
268
304
 
269
305
  <div v-if="loading">Loading cameras...</div>
270
306
 
307
+ <!-- Use computed property for better performance (status string computed once per camera) -->
271
308
  <div class="camera-grid" v-else>
272
- <div v-for="camera in cameras" :key="camera.id" class="camera-card">
309
+ <div v-for="camera in camerasWithStatus" :key="camera.id" class="camera-card">
273
310
  <h3>{{ camera.name }}</h3>
274
- <span :class="camera.status">{{ camera.status }}</span>
311
+ <span :class="camera.statusString">
312
+ {{ camera.statusString || 'unknown' }}
313
+ </span>
275
314
  </div>
276
315
  </div>
277
316
  </div>
@@ -287,8 +326,38 @@ onMounted(fetchCameras)
287
326
  | FORBIDDEN | No permission | Show access denied message |
288
327
  | API_ERROR | Server error | Show error, allow retry |
289
328
 
329
+ ## Camera ID Usage
330
+
331
+ The `camera.id` property is used consistently across all toolkit functions:
332
+ - `getCameras()` returns cameras with `id` property
333
+ - `listFeeds({ deviceId: camera.id })` for feeds
334
+ - `getLiveImage({ cameraId: camera.id })` for images
335
+ - LivePlayer SDK: `{ cameraId: camera.id }` for live video
336
+
337
+ **Note:** Some legacy EEN documentation may refer to "ESN" (Electronic Serial Number). This is outdated terminology - the current API uses `id`. In the toolkit, always use `camera.id`.
338
+
339
+ ## Checking Camera Status for Previews
340
+
341
+ Before loading previews or video, check if the camera is in a viewable state:
342
+
343
+ ```typescript
344
+ function isCameraOnline(status?: CameraStatus | { connectionStatus?: CameraStatus }): boolean {
345
+ // Handle both string status and nested object status
346
+ const statusStr = typeof status === 'string' ? status : status?.connectionStatus
347
+ return statusStr === 'online' || statusStr === 'streaming' || statusStr === 'registered'
348
+ }
349
+
350
+ // Usage
351
+ if (isCameraOnline(camera.status)) {
352
+ // Safe to load preview or video
353
+ } else {
354
+ // Show "Camera offline" message
355
+ }
356
+ ```
357
+
290
358
  ## Constraints
291
359
  - Always check authentication before API calls
292
360
  - Use appropriate status filters to reduce payload
293
361
  - Handle pagination for accounts with many devices
294
362
  - Use include[] to request only needed fields
363
+ - Check camera status before loading previews (offline cameras won't have streams)
@@ -58,12 +58,14 @@ assistant: "I'll use the een-media-agent to diagnose the HLS configuration and a
58
58
  - Modify multipartUrl with query parameters
59
59
  - Use multipartUrl without initMediaSession() first
60
60
  - Assume timestamps are ISO 8601 (they use +00:00 format)
61
+ - Pass ANY arguments to LivePlayer constructor - it MUST be called as `new LivePlayer()` with no arguments
61
62
 
62
63
  **ALWAYS:**
63
64
  - Use getLiveImage() for simple thumbnails
64
65
  - Use initMediaSession() before multipartUrl
65
66
  - Use formatTimestamp() for EEN API timestamps
66
67
  - Check authentication before media operations
68
+ - Pass config to LivePlayer's `start()` method, NOT the constructor
67
69
 
68
70
  ## Choosing the Right Preview Method
69
71
 
@@ -213,28 +215,82 @@ function setupHlsPlayer(videoElement: HTMLVideoElement, hlsUrl: string) {
213
215
 
214
216
  ## Live Video SDK Integration
215
217
 
216
- For full-quality live video, use the EEN Live Video SDK:
218
+ For full-quality live video, use the EEN Live Video Web SDK:
217
219
 
218
220
  ```typescript
219
- // Install: npm install @eencloud/live-video-sdk
221
+ // Install: npm install @een/live-video-web-sdk
220
222
 
221
- import { EENLiveVideo } from '@eencloud/live-video-sdk'
223
+ import LivePlayer from '@een/live-video-web-sdk'
222
224
  import { useAuthStore } from 'een-api-toolkit'
223
225
 
224
- function setupLiveVideo(container: HTMLElement, cameraId: string) {
226
+ // Store player instance for cleanup
227
+ let livePlayer: LivePlayer | null = null
228
+
229
+ async function setupLiveVideo(videoElement: HTMLVideoElement, cameraId: string) {
225
230
  const authStore = useAuthStore()
226
231
 
227
- const player = new EENLiveVideo({
228
- container,
229
- cameraId,
230
- accessToken: authStore.token,
231
- baseUrl: authStore.baseUrl
232
+ // CRITICAL: Create LivePlayer WITHOUT arguments
233
+ livePlayer = new LivePlayer()
234
+
235
+ // CRITICAL: Pass config to start(), NOT to constructor
236
+ await livePlayer.start({
237
+ videoElement, // HTML video element (required)
238
+ cameraId, // Camera device ID (required)
239
+ baseUrl: authStore.baseUrl, // EEN API base URL (required)
240
+ jwt: authStore.token // Auth token (required)
232
241
  })
233
242
 
234
- player.play()
243
+ return livePlayer
244
+ }
245
+
246
+ // Clean up when done
247
+ function stopLiveVideo() {
248
+ if (livePlayer) {
249
+ livePlayer.stop()
250
+ livePlayer = null
251
+ }
252
+ }
253
+ ```
254
+
255
+ ### IMPORTANT: LivePlayer Usage Pattern
256
+
257
+ **CORRECT pattern:**
258
+ ```typescript
259
+ const player = new LivePlayer() // No arguments to constructor
260
+ await player.start(config) // Config passed to start()
261
+ ```
262
+
263
+ **WRONG pattern (will cause "Video Stream is done" errors):**
264
+ ```typescript
265
+ const player = new LivePlayer(config) // DON'T pass config here
266
+ await player.start() // This won't work correctly
267
+ ```
268
+
269
+ ### IMPORTANT: Video Element Must Be in DOM
270
+
271
+ The video element MUST be rendered in the DOM before calling `player.start()`.
272
+
273
+ **Problem:** Using `v-if` to conditionally show the video element means it doesn't exist during loading:
274
+ ```vue
275
+ <!-- WRONG: Video element doesn't exist when loading=true -->
276
+ <div v-if="loading">Loading...</div>
277
+ <video v-else ref="videoRef" /> <!-- Not in DOM during loading! -->
278
+ ```
235
279
 
236
- return player
280
+ **Solution:** Always render the video element, use CSS to hide it:
281
+ ```vue
282
+ <!-- CORRECT: Video element always exists in DOM -->
283
+ <div v-if="loading" class="loading-overlay">Loading...</div>
284
+ <div class="video-container" :class="{ hidden: loading }">
285
+ <video ref="videoRef" /> <!-- Always in DOM -->
286
+ </div>
287
+
288
+ <style>
289
+ .video-container.hidden {
290
+ visibility: hidden;
291
+ position: absolute;
237
292
  }
293
+ </style>
238
294
  ```
239
295
 
240
296
  ## Error Handling
@@ -253,4 +309,7 @@ function setupLiveVideo(container: HTMLElement, cameraId: string) {
253
309
  | Image not loading | Auth not in cookies | Call initMediaSession() first |
254
310
  | Timestamp errors | Wrong format | Use formatTimestamp() |
255
311
  | CORS errors | Direct API access | Use toolkit functions, not direct fetch |
256
- | Black video | HLS auth missing | Configure xhrSetup with token |
312
+ | Black video (HLS) | HLS auth missing | Configure xhrSetup with token |
313
+ | "Video Stream is done" immediately | Config passed to LivePlayer constructor | MUST use `new LivePlayer()` with no args, then `player.start(config)` |
314
+ | "Video element not found" | Video element not in DOM | Ensure video element is rendered (not hidden by v-if) before SDK init |
315
+ | Black video, no errors (LivePlayer) | Video element hidden by v-if | Use CSS visibility/opacity instead of v-if for conditional video display |
package/CHANGELOG.md CHANGED
@@ -2,170 +2,21 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
- ## [0.3.22] - 2026-01-22
5
+ ## [0.3.30] - 2026-01-23
6
6
 
7
7
  ### Release Summary
8
8
 
9
- #### PR #64: Release v0.3.20: Add media buttons to event modal lightboxes
10
- ## Summary
11
-
12
- - Added Preview, HD Image, and Video buttons to event modal lightboxes in vue-events and vue-event-subscriptions example apps
13
- - Implemented proper authentication for media fetching using `getRecordedImage` and HLS player with Authorization headers
14
- - Created `useHlsPlayer` composable for both apps following vue-alerts-metrics pattern
15
-
16
- ## Changes
17
-
18
- - **vue-events**: Updated EventsModal.vue with media buttons and HLS video support
19
- - **vue-event-subscriptions**: Updated LiveEvents.vue with media buttons and HLS video support
20
- - Added hls.js dependency to both example apps
21
- - Created composables/useHlsPlayer.ts in both apps
22
-
23
- ## Test Plan
24
-
25
- - [x] Unit tests: 341 passed
26
- - [x] Build: successful
27
- - [x] E2E tests for all 8 example apps passed (123 total):
28
- - vue-users: 14 passed
29
- - vue-cameras: 13 passed
30
- - vue-bridges: 13 passed
31
- - vue-events: 16 passed
32
- - vue-feeds: 12 passed
33
- - vue-media: 20 passed
34
- - vue-alerts-metrics: 20 passed
35
- - vue-event-subscriptions: 15 passed
36
-
37
- ## Version
38
-
39
- v0.3.20
40
-
41
- ## Commits
42
-
43
- - 8f11b04 feat: Add media buttons to event modal lightboxes in vue-events and vue-event-subscriptions
44
-
45
- 🤖 Generated with [Claude Code](https://claude.com/claude-code)
46
-
47
- #### PR #65: feat: Restructure AI documentation with specialized agents
48
- ## Summary
49
-
50
- Restructures the AI documentation from a single monolithic file into domain-specific documents with specialized Claude agents for context-efficient AI assistance.
51
-
52
- ### Changes
53
-
54
- **Documentation Split (7 files):**
55
- - `AI-CONTEXT.md` - Overview and navigation (entry point)
56
- - `AI-SETUP.md` - Vue 3 scaffolding, Pinia, Vite configuration
57
- - `AI-AUTH.md` - OAuth flow, tokens, route guards
58
- - `AI-USERS.md` - User management API
59
- - `AI-DEVICES.md` - Cameras & Bridges API
60
- - `AI-MEDIA.md` - Media, Feeds, Live Video, HLS
61
- - `AI-EVENTS.md` - Events, Alerts, Metrics, SSE subscriptions
62
-
63
- **Specialized Agents (6 new agents):**
64
- - `een-setup-agent` - Vue 3 project setup assistance
65
- - `een-auth-agent` - OAuth authentication help
66
- - `een-users-agent` - User management features
67
- - `een-devices-agent` - Camera and bridge operations
68
- - `een-media-agent` - Video and media troubleshooting
69
- - `een-events-agent` - Events and real-time streaming
70
-
71
- **CLI Tool:**
72
- - Added `npx een-setup-agents` command for easy agent installation
73
- - Copies agents to `.claude/agents/` for automatic discovery
74
-
75
- **Generation Script:**
76
- - Refactored to generate split files by default
77
- - `--single` flag for legacy monolithic output
78
- - Extracts examples from actual Vue components
79
-
80
- ### Benefits
81
-
82
- | Metric | Before | After (typical task) |
83
- |--------|--------|---------------------|
84
- | Tokens per task | ~15-18K | ~5-8K |
85
- | Context preserved | Limited | Substantial |
86
- | Agent specialization | None | 6 domain agents |
87
-
88
- ## Test Results
89
-
90
- - **Lint:** ✅ Passed (1 pre-existing warning)
91
- - **Unit Tests:** ✅ 341 passed
92
- - **Build:** ✅ Successful
93
- - **E2E Tests:** ✅ 70 passed across 8 example apps
94
-
95
- ## Version
96
-
97
- `0.3.20`
98
-
99
- ## Commits
100
-
101
- - `8e1cc78` feat: Restructure AI documentation into domain-specific files with specialized agents
102
-
103
- ---
104
-
105
- 🤖 Generated with [Claude Code](https://claude.ai/code)
106
-
107
- #### PR #66: Release v0.3.20: AI Documentation Restructure
108
- ## Summary
109
-
110
- This release restructures the AI documentation for improved context efficiency:
111
-
112
- - **Split AI-CONTEXT.md** into 7 domain-specific documents (~2-4K tokens each vs 15K+ monolithic)
113
- - **Added 6 specialized Claude agents** for domain-specific assistance
114
- - **New CLI tool**: `npx een-setup-agents` for easy agent installation in consumer projects
115
- - **Code quality fixes** addressing Gemini code review feedback
116
-
117
- ## Changes
118
-
119
- ### New Files
120
- - `.claude/agents/` - 6 specialized agents (auth, users, devices, media, events, setup)
121
- - `docs/ai-reference/` - 6 domain-specific AI reference documents
122
- - `scripts/setup-agents.ts` - CLI tool for agent installation
123
-
124
- ### Modified
125
- - `docs/AI-CONTEXT.md` - Now serves as overview with navigation
126
- - `scripts/generate-ai-context.ts` - Rewritten to support split file generation
127
- - `package.json` - Added bin entry and updated files array
128
- - `CLAUDE.md` - Added agent documentation section
129
-
130
- ### Commits
131
- - `8e1cc78` feat: Restructure AI documentation into domain-specific files with specialized agents
132
- - `407db5f` fix: Address Gemini code review feedback
133
- - `ecd6cfd` Merge pull request #65
134
-
135
- ## Test Results
136
-
137
- - **Lint**: ✅ Passed (1 pre-existing warning)
138
- - **Unit Tests**: ✅ 341 passed
139
- - **Build**: ✅ Successful
140
- - **E2E Tests**: ✅ 70 passed across 8 example apps
141
-
142
- ## Version
143
-
144
- `0.3.20`
145
-
146
- ---
147
-
148
- 🤖 Generated with [Claude Code](https://claude.ai/code)
149
-
9
+ No PR descriptions available for this release.
150
10
 
151
11
  ### Detailed Changes
152
12
 
153
- #### Features
154
- - feat: Restructure AI documentation into domain-specific files with specialized agents
155
- - feat: Add media buttons to event modal lightboxes in vue-events and vue-event-subscriptions
156
-
157
- #### Bug Fixes
158
- - fix: Add XSS protection for SVG overlays in events agent
159
- - fix: Address Gemini code review feedback
160
- - fix: Address code review feedback from PR #64
161
-
162
13
  #### Other Changes
163
- - chore: Bump version to 0.3.21 and update Husky triggers
164
- - refactor: Move media session state from module-level to Pinia store
14
+ - refactor: Use computed property for camera status in devices agent
15
+ - docs: Document status field requirements in devices agent
165
16
 
166
17
  ### Links
167
18
  - [npm package](https://www.npmjs.com/package/een-api-toolkit)
168
- - [Full Changelog](https://github.com/klaushofrichter/een-api-toolkit/compare/v0.3.20...v0.3.22)
19
+ - [Full Changelog](https://github.com/klaushofrichter/een-api-toolkit/compare/v0.3.28...v0.3.30)
169
20
 
170
21
  ---
171
- *Released: 2026-01-22 19:45:17 CST*
22
+ *Released: 2026-01-23 17:42:54 CST*
package/README.md CHANGED
@@ -176,6 +176,29 @@ if (!cameraError) {
176
176
  | **[AI Context](./docs/AI-CONTEXT.md)** | Single-file reference for AI assistants (also in npm package) |
177
177
  | **[AI Prompts](./docs/Prompts.md)** | Example prompts for generating apps with AI |
178
178
 
179
+ ## Claude Code Agents
180
+
181
+ The toolkit includes specialized [Claude Code](https://docs.anthropic.com/en/docs/claude-code) agents for AI-assisted development. These agents have deep knowledge of the EEN API and toolkit patterns.
182
+
183
+ > **Note:** These agents use Claude Code's specific format. To use with other AI assistants (Gemini CLI, Copilot, etc.), the agent specs would need conversion.
184
+
185
+ **Available agents:**
186
+ - `een-setup-agent` - Project scaffolding, Pinia setup, Vite configuration
187
+ - `een-auth-agent` - OAuth flows, route guards, token management
188
+ - `een-users-agent` - User listing and profile APIs
189
+ - `een-devices-agent` - Camera and bridge management
190
+ - `een-media-agent` - Live video, previews, HLS playback
191
+ - `een-events-agent` - Events, alerts, metrics, SSE subscriptions
192
+
193
+ **Installation:**
194
+ ```bash
195
+ npx een-setup-agents
196
+ ```
197
+
198
+ > **Important:** After running the setup script, **restart Claude Code** for the agents to become available.
199
+
200
+ See the [User Guide](./docs/USER-GUIDE.md#claude-code-agents) for detailed agent documentation.
201
+
179
202
  ## Example Applications
180
203
 
181
204
  The `examples/` directory contains complete Vue 3 applications demonstrating toolkit features: