een-api-toolkit 0.3.30 → 0.3.38
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/docs-accuracy-reviewer.md +15 -3
- package/.claude/agents/een-auth-agent.md +131 -0
- package/.claude/agents/een-devices-agent.md +10 -7
- package/.claude/agents/een-events-agent.md +98 -0
- package/.claude/agents/een-grouping-agent.md +394 -0
- package/.claude/agents/een-media-agent.md +25 -5
- package/CHANGELOG.md +77 -6
- package/README.md +5 -3
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +561 -0
- package/dist/index.js +388 -218
- package/dist/index.js.map +1 -1
- package/docs/AI-CONTEXT.md +13 -1
- package/docs/ai-reference/AI-AUTH.md +1 -1
- package/docs/ai-reference/AI-DEVICES.md +1 -1
- package/docs/ai-reference/AI-EVENTS.md +1 -1
- package/docs/ai-reference/AI-GROUPING.md +411 -0
- package/docs/ai-reference/AI-MEDIA.md +1 -1
- package/docs/ai-reference/AI-SETUP.md +1 -1
- package/docs/ai-reference/AI-USERS.md +1 -1
- package/examples/vue-alerts-metrics/README.md +2 -0
- package/examples/vue-alerts-metrics/alert-metrics-screenshot.png +0 -0
- package/examples/vue-alerts-metrics/e2e/auth.spec.ts +1 -1
- package/examples/vue-alerts-metrics/package-lock.json +17 -14
- package/examples/vue-alerts-metrics/package.json +1 -1
- package/examples/vue-bridges/package-lock.json +21 -15
- package/examples/vue-bridges/package.json +1 -1
- package/examples/vue-cameras/package-lock.json +21 -15
- package/examples/vue-cameras/package.json +1 -1
- package/examples/vue-event-subscriptions/README.md +2 -0
- package/examples/vue-event-subscriptions/event-subscriptions-screenshot.png +0 -0
- package/examples/vue-event-subscriptions/package-lock.json +17 -14
- package/examples/vue-event-subscriptions/package.json +1 -1
- package/examples/vue-events/events-screenshot.png +0 -0
- package/examples/vue-events/package-lock.json +17 -14
- package/examples/vue-events/package.json +1 -1
- package/examples/vue-feeds/package-lock.json +21 -15
- package/examples/vue-feeds/package.json +1 -1
- package/examples/vue-layouts/.env.example +12 -0
- package/examples/vue-layouts/README.md +320 -0
- package/examples/vue-layouts/e2e/app.spec.ts +76 -0
- package/examples/vue-layouts/e2e/auth.spec.ts +264 -0
- package/examples/vue-layouts/index.html +13 -0
- package/examples/vue-layouts/layouts-screenshot.png +0 -0
- package/examples/vue-layouts/package-lock.json +1722 -0
- package/examples/vue-layouts/package.json +28 -0
- package/examples/vue-layouts/playwright.config.ts +47 -0
- package/examples/vue-layouts/src/App.vue +124 -0
- package/examples/vue-layouts/src/components/LayoutModal.vue +456 -0
- package/examples/vue-layouts/src/main.ts +25 -0
- package/examples/vue-layouts/src/router/index.ts +62 -0
- package/examples/vue-layouts/src/views/Callback.vue +76 -0
- package/examples/vue-layouts/src/views/Home.vue +188 -0
- package/examples/vue-layouts/src/views/Layouts.vue +355 -0
- package/examples/vue-layouts/src/views/Login.vue +33 -0
- package/examples/vue-layouts/src/views/Logout.vue +59 -0
- package/examples/vue-layouts/src/vite-env.d.ts +12 -0
- package/examples/vue-layouts/tsconfig.json +21 -0
- package/examples/vue-layouts/tsconfig.node.json +10 -0
- package/examples/vue-layouts/vite.config.ts +12 -0
- package/examples/vue-media/e2e/auth.spec.ts +35 -1
- package/examples/vue-media/media-screenshot.png +0 -0
- package/examples/vue-media/package-lock.json +19 -14
- package/examples/vue-media/package.json +1 -1
- package/examples/vue-users/package-lock.json +21 -16
- package/examples/vue-users/package.json +2 -2
- package/package.json +2 -2
- package/scripts/setup-agents.ts +9 -7
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: een-grouping-agent
|
|
3
|
+
description: |
|
|
4
|
+
Use this agent when working with layouts (camera groupings): listing layouts,
|
|
5
|
+
creating/editing layouts, managing panes, or implementing layout selection UI
|
|
6
|
+
with the een-api-toolkit.
|
|
7
|
+
model: inherit
|
|
8
|
+
color: purple
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
You are an expert in layout (camera grouping) management with the een-api-toolkit.
|
|
12
|
+
|
|
13
|
+
## Examples
|
|
14
|
+
|
|
15
|
+
<example>
|
|
16
|
+
Context: User wants to display a layout list.
|
|
17
|
+
user: "How do I show all layouts in a grid?"
|
|
18
|
+
assistant: "I'll use the een-grouping-agent to help implement layout listing using getLayouts()."
|
|
19
|
+
<Task tool call to launch een-grouping-agent>
|
|
20
|
+
</example>
|
|
21
|
+
|
|
22
|
+
<example>
|
|
23
|
+
Context: User wants to create a new layout.
|
|
24
|
+
user: "How do I create a layout with 3 cameras?"
|
|
25
|
+
assistant: "I'll use the een-grouping-agent to implement layout creation with createLayout()."
|
|
26
|
+
<Task tool call to launch een-grouping-agent>
|
|
27
|
+
</example>
|
|
28
|
+
|
|
29
|
+
<example>
|
|
30
|
+
Context: User wants to edit layout settings.
|
|
31
|
+
user: "How do I change the number of columns in a layout?"
|
|
32
|
+
assistant: "I'll use the een-grouping-agent to help update layout settings with updateLayout()."
|
|
33
|
+
<Task tool call to launch een-grouping-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-GROUPING.md (primary reference)
|
|
40
|
+
|
|
41
|
+
## Reference Examples
|
|
42
|
+
- examples/vue-layouts/ (complete CRUD with modal)
|
|
43
|
+
|
|
44
|
+
## Your Capabilities
|
|
45
|
+
1. List and filter layouts with getLayouts()
|
|
46
|
+
2. Get layout details with getLayout()
|
|
47
|
+
3. Create new layouts with createLayout()
|
|
48
|
+
4. Update layouts with updateLayout()
|
|
49
|
+
5. Delete layouts with deleteLayout()
|
|
50
|
+
6. Manage layout panes (add/remove cameras)
|
|
51
|
+
7. Configure layout settings (columns, aspect ratio, borders)
|
|
52
|
+
|
|
53
|
+
## Key Types
|
|
54
|
+
|
|
55
|
+
### Layout Interface
|
|
56
|
+
```typescript
|
|
57
|
+
interface Layout {
|
|
58
|
+
id: string
|
|
59
|
+
name: string
|
|
60
|
+
accountId: string
|
|
61
|
+
panes: LayoutPane[]
|
|
62
|
+
settings: LayoutSettings
|
|
63
|
+
effectivePermissions?: LayoutPermissions
|
|
64
|
+
resourceCounts?: { cameras?: number }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface LayoutPane {
|
|
68
|
+
id: number
|
|
69
|
+
name: string
|
|
70
|
+
type: 'preview' | 'compositePreview'
|
|
71
|
+
size: 1 | 2 | 3
|
|
72
|
+
cameraId: string
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
interface LayoutSettings {
|
|
76
|
+
showCameraBorder: boolean
|
|
77
|
+
showCameraName: boolean
|
|
78
|
+
cameraAspectRatio: '16x9' | '4x3'
|
|
79
|
+
paneColumns: number // 1-6
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### ListLayoutsParams
|
|
84
|
+
```typescript
|
|
85
|
+
interface ListLayoutsParams {
|
|
86
|
+
pageSize?: number
|
|
87
|
+
pageToken?: string
|
|
88
|
+
include?: ('effectivePermissions' | 'resourceCounts' | 'resourceStatusCounts')[]
|
|
89
|
+
name__contains?: string
|
|
90
|
+
q?: string
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Key Functions
|
|
95
|
+
|
|
96
|
+
### getLayouts()
|
|
97
|
+
List layouts with optional filters.
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
import { getLayouts, type Layout, type ListLayoutsParams } from 'een-api-toolkit'
|
|
101
|
+
|
|
102
|
+
const layouts = ref<Layout[]>([])
|
|
103
|
+
|
|
104
|
+
// Get all layouts
|
|
105
|
+
async function fetchLayouts() {
|
|
106
|
+
const result = await getLayouts({
|
|
107
|
+
include: ['resourceCounts', 'effectivePermissions'],
|
|
108
|
+
pageSize: 100
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
if (result.data) {
|
|
112
|
+
layouts.value = result.data.results
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Search layouts by name
|
|
117
|
+
async function searchLayouts(query: string) {
|
|
118
|
+
const result = await getLayouts({
|
|
119
|
+
q: query,
|
|
120
|
+
include: ['resourceCounts']
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
if (result.data) {
|
|
124
|
+
layouts.value = result.data.results
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### getLayout(id, params?)
|
|
130
|
+
Get a specific layout:
|
|
131
|
+
```typescript
|
|
132
|
+
import { getLayout, type Layout } from 'een-api-toolkit'
|
|
133
|
+
|
|
134
|
+
async function fetchLayout(layoutId: string) {
|
|
135
|
+
const result = await getLayout(layoutId, {
|
|
136
|
+
include: ['effectivePermissions', 'resourceStatusCounts']
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
if (result.error) {
|
|
140
|
+
if (result.error.code === 'NOT_FOUND') {
|
|
141
|
+
console.error('Layout not found')
|
|
142
|
+
}
|
|
143
|
+
return null
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return result.data
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### createLayout(params)
|
|
151
|
+
Create a new layout:
|
|
152
|
+
```typescript
|
|
153
|
+
import { createLayout, type CreateLayoutParams, type LayoutSettings } from 'een-api-toolkit'
|
|
154
|
+
|
|
155
|
+
async function handleCreateLayout(name: string, cameraIds: string[]) {
|
|
156
|
+
const settings: LayoutSettings = {
|
|
157
|
+
showCameraBorder: true,
|
|
158
|
+
showCameraName: true,
|
|
159
|
+
cameraAspectRatio: '16x9',
|
|
160
|
+
paneColumns: 3
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const panes = cameraIds.map((cameraId, index) => ({
|
|
164
|
+
id: index + 1,
|
|
165
|
+
name: `Camera ${index + 1}`,
|
|
166
|
+
type: 'preview' as const,
|
|
167
|
+
size: 1 as const,
|
|
168
|
+
cameraId
|
|
169
|
+
}))
|
|
170
|
+
|
|
171
|
+
const result = await createLayout({
|
|
172
|
+
name,
|
|
173
|
+
settings,
|
|
174
|
+
panes
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
if (result.error) {
|
|
178
|
+
console.error('Failed to create layout:', result.error.message)
|
|
179
|
+
return null
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return result.data
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### updateLayout(id, params)
|
|
187
|
+
Update an existing layout:
|
|
188
|
+
```typescript
|
|
189
|
+
import { updateLayout, type UpdateLayoutParams } from 'een-api-toolkit'
|
|
190
|
+
|
|
191
|
+
async function handleUpdateLayout(layoutId: string, updates: UpdateLayoutParams) {
|
|
192
|
+
const result = await updateLayout(layoutId, updates)
|
|
193
|
+
|
|
194
|
+
if (result.error) {
|
|
195
|
+
console.error('Failed to update layout:', result.error.message)
|
|
196
|
+
return false
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return true
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Update name
|
|
203
|
+
await handleUpdateLayout('layout-123', { name: 'New Name' })
|
|
204
|
+
|
|
205
|
+
// Update settings
|
|
206
|
+
await handleUpdateLayout('layout-123', {
|
|
207
|
+
settings: { paneColumns: 4 }
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
// Replace panes
|
|
211
|
+
await handleUpdateLayout('layout-123', {
|
|
212
|
+
panes: [
|
|
213
|
+
{ id: 1, name: 'New Pane', type: 'preview', size: 1, cameraId: 'cam-123' }
|
|
214
|
+
]
|
|
215
|
+
})
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### deleteLayout(id)
|
|
219
|
+
Delete a layout:
|
|
220
|
+
```typescript
|
|
221
|
+
import { deleteLayout } from 'een-api-toolkit'
|
|
222
|
+
|
|
223
|
+
async function handleDeleteLayout(layoutId: string) {
|
|
224
|
+
const result = await deleteLayout(layoutId)
|
|
225
|
+
|
|
226
|
+
if (result.error) {
|
|
227
|
+
if (result.error.code === 'FORBIDDEN') {
|
|
228
|
+
console.error('No permission to delete this layout')
|
|
229
|
+
}
|
|
230
|
+
return false
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return true
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Complete Layout Manager Component
|
|
238
|
+
|
|
239
|
+
```vue
|
|
240
|
+
<script setup lang="ts">
|
|
241
|
+
import { ref, onMounted } from 'vue'
|
|
242
|
+
import {
|
|
243
|
+
getLayouts,
|
|
244
|
+
createLayout,
|
|
245
|
+
updateLayout,
|
|
246
|
+
deleteLayout,
|
|
247
|
+
type Layout,
|
|
248
|
+
type LayoutSettings,
|
|
249
|
+
type ListLayoutsParams
|
|
250
|
+
} from 'een-api-toolkit'
|
|
251
|
+
|
|
252
|
+
const layouts = ref<Layout[]>([])
|
|
253
|
+
const loading = ref(false)
|
|
254
|
+
|
|
255
|
+
const defaultSettings: LayoutSettings = {
|
|
256
|
+
showCameraBorder: true,
|
|
257
|
+
showCameraName: true,
|
|
258
|
+
cameraAspectRatio: '16x9',
|
|
259
|
+
paneColumns: 3
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async function fetchLayouts() {
|
|
263
|
+
loading.value = true
|
|
264
|
+
|
|
265
|
+
const params: ListLayoutsParams = {
|
|
266
|
+
include: ['effectivePermissions', 'resourceCounts'],
|
|
267
|
+
pageSize: 100
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const result = await getLayouts(params)
|
|
271
|
+
|
|
272
|
+
if (result.data) {
|
|
273
|
+
layouts.value = result.data.results
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
loading.value = false
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
async function handleCreate(name: string) {
|
|
280
|
+
const result = await createLayout({
|
|
281
|
+
name,
|
|
282
|
+
settings: defaultSettings,
|
|
283
|
+
panes: []
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
if (result.data) {
|
|
287
|
+
await fetchLayouts()
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return result
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
async function handleUpdate(layoutId: string, name: string) {
|
|
294
|
+
const result = await updateLayout(layoutId, { name })
|
|
295
|
+
|
|
296
|
+
if (!result.error) {
|
|
297
|
+
await fetchLayouts()
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return result
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async function handleDelete(layoutId: string) {
|
|
304
|
+
if (!confirm('Delete this layout?')) return
|
|
305
|
+
|
|
306
|
+
const result = await deleteLayout(layoutId)
|
|
307
|
+
|
|
308
|
+
if (!result.error) {
|
|
309
|
+
await fetchLayouts()
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return result
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
onMounted(fetchLayouts)
|
|
316
|
+
</script>
|
|
317
|
+
|
|
318
|
+
<template>
|
|
319
|
+
<div class="layout-manager">
|
|
320
|
+
<div v-if="loading">Loading layouts...</div>
|
|
321
|
+
|
|
322
|
+
<div v-else class="layout-grid">
|
|
323
|
+
<div v-for="layout in layouts" :key="layout.id" class="layout-card">
|
|
324
|
+
<h3>{{ layout.name }}</h3>
|
|
325
|
+
<p>{{ layout.panes.length }} panes</p>
|
|
326
|
+
<p>{{ layout.settings.paneColumns }} columns</p>
|
|
327
|
+
|
|
328
|
+
<div class="actions">
|
|
329
|
+
<button v-if="layout.effectivePermissions?.edit" @click="handleUpdate(layout.id, 'New Name')">
|
|
330
|
+
Edit
|
|
331
|
+
</button>
|
|
332
|
+
<button v-if="layout.effectivePermissions?.delete" @click="handleDelete(layout.id)">
|
|
333
|
+
Delete
|
|
334
|
+
</button>
|
|
335
|
+
</div>
|
|
336
|
+
</div>
|
|
337
|
+
</div>
|
|
338
|
+
</div>
|
|
339
|
+
</template>
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## Error Handling
|
|
343
|
+
|
|
344
|
+
| Error Code | Meaning | Action |
|
|
345
|
+
|------------|---------|--------|
|
|
346
|
+
| AUTH_REQUIRED | Not authenticated | Redirect to login |
|
|
347
|
+
| NOT_FOUND | Layout doesn't exist | Show "not found" message |
|
|
348
|
+
| FORBIDDEN | No permission | Show access denied message |
|
|
349
|
+
| VALIDATION_ERROR | Invalid input | Show validation error |
|
|
350
|
+
| API_ERROR | Server error | Show error, allow retry |
|
|
351
|
+
|
|
352
|
+
## Layout Pane Management
|
|
353
|
+
|
|
354
|
+
When adding/removing panes, remember:
|
|
355
|
+
- Each pane needs a unique `id` within the layout
|
|
356
|
+
- `cameraId` links the pane to a camera
|
|
357
|
+
- `size` affects grid positioning (1=1x1, 2=2x2, 3=3x3)
|
|
358
|
+
- Use `type: 'preview'` for standard camera views
|
|
359
|
+
|
|
360
|
+
```typescript
|
|
361
|
+
// Add a pane to existing layout
|
|
362
|
+
function addPane(layout: Layout, cameraId: string) {
|
|
363
|
+
const newId = layout.panes.length > 0
|
|
364
|
+
? Math.max(...layout.panes.map(p => p.id)) + 1
|
|
365
|
+
: 1
|
|
366
|
+
|
|
367
|
+
const newPane = {
|
|
368
|
+
id: newId,
|
|
369
|
+
name: `Camera ${newId}`,
|
|
370
|
+
type: 'preview' as const,
|
|
371
|
+
size: 1 as const,
|
|
372
|
+
cameraId
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return updateLayout(layout.id, {
|
|
376
|
+
panes: [...layout.panes, newPane]
|
|
377
|
+
})
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Remove a pane
|
|
381
|
+
function removePane(layout: Layout, paneId: number) {
|
|
382
|
+
return updateLayout(layout.id, {
|
|
383
|
+
panes: layout.panes.filter(p => p.id !== paneId)
|
|
384
|
+
})
|
|
385
|
+
}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
## Constraints
|
|
389
|
+
- Always check authentication before API calls
|
|
390
|
+
- Check effectivePermissions before showing edit/delete buttons
|
|
391
|
+
- Layout name is required for create/update
|
|
392
|
+
- Settings object is required for create
|
|
393
|
+
- PATCH returns 204 (void), not the updated layout
|
|
394
|
+
- DELETE returns 204 (void) on success
|
|
@@ -76,6 +76,15 @@ assistant: "I'll use the een-media-agent to diagnose the HLS configuration and a
|
|
|
76
76
|
| Full-quality live video | Live Video SDK | Full resolution, lowest latency |
|
|
77
77
|
| Recorded video playback | HLS via `listMedia()` | Seek capability, standard player |
|
|
78
78
|
|
|
79
|
+
**CRITICAL: Main feeds do NOT support multipartUrl**
|
|
80
|
+
|
|
81
|
+
The EEN API only returns `multipartUrl` for **preview feeds** (`type: 'preview'`), not main feeds (`type: 'main'`).
|
|
82
|
+
|
|
83
|
+
- **Preview feeds** → Use `multipartUrl` (MJPEG in `<img>` element)
|
|
84
|
+
- **Main feeds** → Use **Live Video SDK** (full HD in `<video>` element)
|
|
85
|
+
|
|
86
|
+
If you need HD quality video, you MUST use the Live Video SDK. Do not attempt to use `multipartUrl` with main feeds - it won't work.
|
|
87
|
+
|
|
79
88
|
## Key Functions
|
|
80
89
|
|
|
81
90
|
### getLiveImage(cameraId)
|
|
@@ -117,6 +126,13 @@ async function setupMediaSession() {
|
|
|
117
126
|
```
|
|
118
127
|
|
|
119
128
|
### Using multipartUrl (MJPEG Stream)
|
|
129
|
+
|
|
130
|
+
**Feed Types:**
|
|
131
|
+
| Feed Type | Use Case | Quality |
|
|
132
|
+
|-----------|----------|---------|
|
|
133
|
+
| `preview` | Camera sidebar thumbnails, grids | Lower bandwidth, smaller resolution |
|
|
134
|
+
| `main` | Primary video player, HD viewing | Full quality, higher bandwidth |
|
|
135
|
+
|
|
120
136
|
```typescript
|
|
121
137
|
// MUST call initMediaSession() first!
|
|
122
138
|
import { listFeeds, initMediaSession } from 'een-api-toolkit'
|
|
@@ -125,14 +141,18 @@ onMounted(async () => {
|
|
|
125
141
|
// Step 1: Initialize media session
|
|
126
142
|
await initMediaSession()
|
|
127
143
|
|
|
128
|
-
// Step 2: Get feeds
|
|
129
|
-
const result = await listFeeds({
|
|
144
|
+
// Step 2: Get feeds - specify type for desired quality
|
|
145
|
+
const result = await listFeeds({
|
|
146
|
+
deviceId: props.camera.id,
|
|
147
|
+
type: 'preview', // 'preview' for thumbnails, 'main' for HD
|
|
148
|
+
include: ['multipartUrl'] // Request multipartUrl to be included
|
|
149
|
+
})
|
|
130
150
|
|
|
131
151
|
if (result.data) {
|
|
132
|
-
const
|
|
133
|
-
if (
|
|
152
|
+
const feed = result.data.results?.find(f => f.multipartUrl)
|
|
153
|
+
if (feed?.multipartUrl) {
|
|
134
154
|
// Step 3: Use multipartUrl directly - DO NOT modify it
|
|
135
|
-
previewImageUrl.value =
|
|
155
|
+
previewImageUrl.value = feed.multipartUrl
|
|
136
156
|
}
|
|
137
157
|
}
|
|
138
158
|
})
|
package/CHANGELOG.md
CHANGED
|
@@ -2,21 +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.38] - 2026-01-24
|
|
6
6
|
|
|
7
7
|
### Release Summary
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
#### PR #74: fix: Add missing agent, fix E2E test, and improve CI stability
|
|
10
|
+
## Summary
|
|
11
|
+
- Add missing `een-grouping-agent` to the setup-agents.ts script
|
|
12
|
+
- Fix timezone issue in datetime persistence E2E test (vue-media)
|
|
13
|
+
- Increase AUTH_COMPLETE timeout for better CI stability
|
|
14
|
+
|
|
15
|
+
## Changes
|
|
16
|
+
- `3f04224` fix: Fix timezone issue in datetime persistence E2E test
|
|
17
|
+
- `fbf300c` fix: Add missing een-grouping-agent to setup script
|
|
18
|
+
- `ea6ba9a` fix: Increase AUTH_COMPLETE timeout for CI stability
|
|
19
|
+
|
|
20
|
+
## Test Results
|
|
21
|
+
- ✅ Lint: passed
|
|
22
|
+
- ✅ Unit tests: 378 passed
|
|
23
|
+
- ✅ Build: successful
|
|
24
|
+
- ✅ E2E tests: 137 passed across 9 example apps
|
|
25
|
+
|
|
26
|
+
## Version
|
|
27
|
+
`0.3.35`
|
|
28
|
+
|
|
29
|
+
🤖 Generated with [Claude Code](https://claude.ai/code)
|
|
30
|
+
|
|
31
|
+
#### PR #75: fix: Skip datetime persistence test in CI environments
|
|
32
|
+
## Summary
|
|
33
|
+
Skip the datetime persistence E2E test in CI environments to avoid timezone-related flaky failures.
|
|
34
|
+
|
|
35
|
+
## Changes
|
|
36
|
+
- `fa1d667` fix: Skip datetime persistence test in CI environments
|
|
37
|
+
|
|
38
|
+
## Details
|
|
39
|
+
The datetime persistence test relies on local timezone calculations that produce inconsistent results between CI (UTC) and local development environments. The test passes locally but fails in CI due to 1-hour timezone offsets.
|
|
40
|
+
|
|
41
|
+
## Version
|
|
42
|
+
`0.3.35`
|
|
43
|
+
|
|
44
|
+
🤖 Generated with [Claude Code](https://claude.ai/code)
|
|
45
|
+
|
|
46
|
+
#### PR #77: fix: UTC timezone for datetime test and include examples in version bump
|
|
47
|
+
## Summary
|
|
48
|
+
- Force UTC timezone in datetime persistence test for consistent CI/local behavior
|
|
49
|
+
- Include examples folder in Husky version bump trigger
|
|
50
|
+
|
|
51
|
+
## Changes
|
|
52
|
+
- `3641923` fix: Force UTC timezone in datetime persistence test for CI consistency
|
|
53
|
+
- `3a40255` chore: Include examples folder in version bump trigger
|
|
54
|
+
- `0a99fb8` chore: Bump version to 0.3.37
|
|
55
|
+
|
|
56
|
+
## Details
|
|
57
|
+
|
|
58
|
+
### UTC Timezone Fix
|
|
59
|
+
Instead of skipping the datetime persistence test in CI, the browser is now forced to use UTC timezone by overriding `Date.getTimezoneOffset()` via Playwright's `addInitScript`. This ensures consistent behavior across CI (UTC) and local development environments.
|
|
60
|
+
|
|
61
|
+
Addresses issue #76.
|
|
62
|
+
|
|
63
|
+
### Husky Update
|
|
64
|
+
Changes to `examples/**/*` now trigger a package version increment, ensuring example app updates are properly versioned.
|
|
65
|
+
|
|
66
|
+
## Version
|
|
67
|
+
`0.3.37`
|
|
68
|
+
|
|
69
|
+
🤖 Generated with [Claude Code](https://claude.ai/code)
|
|
70
|
+
|
|
10
71
|
|
|
11
72
|
### Detailed Changes
|
|
12
73
|
|
|
74
|
+
#### Bug Fixes
|
|
75
|
+
- fix: Skip datetime persistence test in CI
|
|
76
|
+
- fix: Force UTC timezone in datetime persistence test for CI consistency
|
|
77
|
+
- fix: Skip datetime persistence test in CI environments
|
|
78
|
+
- fix: Fix timezone issue in datetime persistence E2E test
|
|
79
|
+
- fix: Add missing een-grouping-agent to setup script
|
|
80
|
+
|
|
13
81
|
#### Other Changes
|
|
14
|
-
-
|
|
15
|
-
-
|
|
82
|
+
- chore: Bump version to 0.3.36
|
|
83
|
+
- chore: Include examples folder in version bump trigger
|
|
84
|
+
- Update examples/vue-media/e2e/auth.spec.ts
|
|
85
|
+
- refactor: Address Gemini review - use shared formatDateTimeLocal utility
|
|
86
|
+
- docs: Add een-grouping-agent to CLAUDE.md and improve E2E test
|
|
16
87
|
|
|
17
88
|
### Links
|
|
18
89
|
- [npm package](https://www.npmjs.com/package/een-api-toolkit)
|
|
19
|
-
- [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.35...v0.3.38)
|
|
20
91
|
|
|
21
92
|
---
|
|
22
|
-
*Released: 2026-01-
|
|
93
|
+
*Released: 2026-01-24 15:57:26 CST*
|
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ This repository is provided as-is without any warranty, functionality guarantee,
|
|
|
16
16
|
|
|
17
17
|
## Key Features
|
|
18
18
|
|
|
19
|
-
- **Plain Async Functions** - Simple API calls with `getCurrentUser()`, `getUsers()`, `
|
|
19
|
+
- **Plain Async Functions** - Simple API calls with `getCurrentUser()`, `getUsers()`, `getCameras()`, `getLayouts()`, and more
|
|
20
20
|
- **Secure OAuth** - Token management via proxy (refresh tokens never exposed to client)
|
|
21
21
|
- **Type-Safe** - Full TypeScript types from OpenAPI spec
|
|
22
22
|
- **Predictable Errors** - Always returns `{data, error}`, no exceptions thrown
|
|
@@ -140,8 +140,8 @@ if (!cameraError) {
|
|
|
140
140
|
│ │ import from 'een-api-toolkit' │ │
|
|
141
141
|
│ │ ┌────────────────────────────────────┐ │ │
|
|
142
142
|
│ │ │ Plain Async Functions │ │ │
|
|
143
|
-
│ │ │
|
|
144
|
-
│ │ │
|
|
143
|
+
│ │ │ getUsers(), getCameras(), etc. │ │ │
|
|
144
|
+
│ │ │ getLayouts(), createLayout()... │ │ │
|
|
145
145
|
│ │ └────────────────────────────────────┘ │ │
|
|
146
146
|
│ └────────────────────────────────────────────────────────────────┘ │
|
|
147
147
|
│ │ │
|
|
@@ -189,6 +189,7 @@ The toolkit includes specialized [Claude Code](https://docs.anthropic.com/en/doc
|
|
|
189
189
|
- `een-devices-agent` - Camera and bridge management
|
|
190
190
|
- `een-media-agent` - Live video, previews, HLS playback
|
|
191
191
|
- `een-events-agent` - Events, alerts, metrics, SSE subscriptions
|
|
192
|
+
- `een-grouping-agent` - Layouts CRUD operations, camera pane management
|
|
192
193
|
|
|
193
194
|
**Installation:**
|
|
194
195
|
```bash
|
|
@@ -208,6 +209,7 @@ The `examples/` directory contains complete Vue 3 applications demonstrating too
|
|
|
208
209
|
| **[vue-users](./examples/vue-users/)** | User management with pagination | `getUsers()`, `getCurrentUser()` |
|
|
209
210
|
| **[vue-cameras](./examples/vue-cameras/)** | Camera listing with status filters | `getCameras()` |
|
|
210
211
|
| **[vue-bridges](./examples/vue-bridges/)** | Bridge listing with device info | `getBridges()` |
|
|
212
|
+
| **[vue-layouts](./examples/vue-layouts/)** | Layout CRUD with camera panes | `getLayouts()`, `createLayout()`, `updateLayout()`, `deleteLayout()` |
|
|
211
213
|
| **[vue-media](./examples/vue-media/)** | Live and recorded image viewing | `getCameras()`, `getLiveImage()`, `getRecordedImage()` |
|
|
212
214
|
| **[vue-feeds](./examples/vue-feeds/)** | Live video streaming with preview and main streams | `getCameras()`, `listFeeds()`, `initMediaSession()` |
|
|
213
215
|
| **[vue-events](./examples/vue-events/)** | Event listing with bounding box overlays | `listEvents()`, `listEventTypes()`, `listEventFieldValues()`, `getRecordedImage()` |
|