een-api-toolkit 0.3.38 → 0.3.46

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.
Files changed (55) hide show
  1. package/.claude/agents/docs-accuracy-reviewer.md +35 -5
  2. package/.claude/agents/een-auth-agent.md +28 -0
  3. package/.claude/agents/een-automations-agent.md +264 -0
  4. package/.claude/agents/een-devices-agent.md +5 -7
  5. package/.claude/agents/een-events-agent.md +40 -18
  6. package/.claude/agents/een-media-agent.md +12 -15
  7. package/.claude/agents/een-setup-agent.md +32 -0
  8. package/.claude/agents/een-users-agent.md +2 -2
  9. package/CHANGELOG.md +9 -75
  10. package/dist/index.cjs +3 -3
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.ts +815 -0
  13. package/dist/index.js +986 -719
  14. package/dist/index.js.map +1 -1
  15. package/docs/AI-CONTEXT.md +17 -1
  16. package/docs/ai-reference/AI-AUTH.md +42 -1
  17. package/docs/ai-reference/AI-AUTOMATIONS.md +833 -0
  18. package/docs/ai-reference/AI-DEVICES.md +1 -1
  19. package/docs/ai-reference/AI-EVENTS.md +2 -2
  20. package/docs/ai-reference/AI-GROUPING.md +128 -66
  21. package/docs/ai-reference/AI-MEDIA.md +1 -1
  22. package/docs/ai-reference/AI-SETUP.md +1 -1
  23. package/docs/ai-reference/AI-USERS.md +1 -1
  24. package/examples/vue-alerts-metrics/src/App.vue +7 -1
  25. package/examples/vue-automations/.env.example +11 -0
  26. package/examples/vue-automations/README.md +205 -0
  27. package/examples/vue-automations/e2e/app.spec.ts +83 -0
  28. package/examples/vue-automations/e2e/auth.spec.ts +468 -0
  29. package/examples/vue-automations/index.html +13 -0
  30. package/examples/vue-automations/package-lock.json +1722 -0
  31. package/examples/vue-automations/package.json +29 -0
  32. package/examples/vue-automations/playwright.config.ts +46 -0
  33. package/examples/vue-automations/src/App.vue +122 -0
  34. package/examples/vue-automations/src/main.ts +23 -0
  35. package/examples/vue-automations/src/router/index.ts +61 -0
  36. package/examples/vue-automations/src/views/Automations.vue +692 -0
  37. package/examples/vue-automations/src/views/Callback.vue +76 -0
  38. package/examples/vue-automations/src/views/Home.vue +172 -0
  39. package/examples/vue-automations/src/views/Login.vue +33 -0
  40. package/examples/vue-automations/src/views/Logout.vue +66 -0
  41. package/examples/vue-automations/src/vite-env.d.ts +1 -0
  42. package/examples/vue-automations/tsconfig.json +21 -0
  43. package/examples/vue-automations/tsconfig.node.json +10 -0
  44. package/examples/vue-automations/vite.config.ts +12 -0
  45. package/examples/vue-bridges/e2e/auth.spec.ts +97 -0
  46. package/examples/vue-bridges/src/App.vue +7 -1
  47. package/examples/vue-cameras/src/App.vue +7 -1
  48. package/examples/vue-event-subscriptions/src/App.vue +7 -1
  49. package/examples/vue-event-subscriptions/src/views/LiveEvents.vue +1 -1
  50. package/examples/vue-events/src/App.vue +7 -1
  51. package/examples/vue-layouts/src/App.vue +7 -1
  52. package/examples/vue-users/package-lock.json +2 -2
  53. package/examples/vue-users/package.json +1 -1
  54. package/examples/vue-users/src/App.vue +7 -1
  55. package/package.json +1 -1
@@ -31,6 +31,13 @@ assistant: "I'll use the docs-accuracy-reviewer agent to verify the README and a
31
31
  <Task tool call to launch docs-accuracy-reviewer agent>
32
32
  </example>
33
33
 
34
+ <example>
35
+ Context: User wants to verify Claude agent definitions are accurate.
36
+ user: "Check if the agent files in .claude/agents match the actual API"
37
+ assistant: "I'll use the docs-accuracy-reviewer agent to verify the een-* agent files have correct function signatures, parameter names, and code examples."
38
+ <Task tool call to launch docs-accuracy-reviewer agent>
39
+ </example>
40
+
34
41
  ## Your Core Responsibilities
35
42
 
36
43
  1. **Function and Feature Verification**: Cross-reference every documented function, method, API, and feature against the actual source code. Verify that:
@@ -62,8 +69,8 @@ assistant: "I'll use the docs-accuracy-reviewer agent to verify the README and a
62
69
  1. **Discovery Phase**:
63
70
  - List all markdown files in the project (README.md, docs/**, CLAUDE.md, etc.)
64
71
  - **Scan ALL example directories** (`examples/*/README.md`) - do not skip any
65
- - Check agent files in `.claude/agents/*.md`
66
- - Identify the source code structure for cross-referencing
72
+ - **Check ALL agent files** in `.claude/agents/een-*.md` - verify function signatures and code examples against actual implementations
73
+ - Identify the source code structure for cross-referencing (especially `src/index.ts` exports, `src/*/service.ts` implementations, and `src/types/*.ts` definitions)
67
74
 
68
75
  2. **Analysis Phase**:
69
76
  - Read each documentation file thoroughly
@@ -115,6 +122,18 @@ assistant: "I'll use the docs-accuracy-reviewer agent to verify the README and a
115
122
  - Verify .env.example matches documentation
116
123
  - Check that all required secrets are documented
117
124
 
125
+ ### For Claude Agent Files (`.claude/agents/een-*.md`):
126
+ - **Function Signatures**: Verify all documented function signatures match actual implementations in `src/*/service.ts`
127
+ - **Parameter Names**: Check that parameter names in examples match the actual API (e.g., `deviceId` not `cameraId`, `userId` as direct param not `{ id: userId }`)
128
+ - **Result Properties**: Verify result property names (e.g., `imageData` not `dataUrl`)
129
+ - **Type Definitions**: Cross-reference documented types against `src/types/*.ts`
130
+ - **Filter Parameters**: Verify filter parameter names use correct suffixes (e.g., `startTimestamp__gte` not `startTimestamp`)
131
+ - **Include Options**: Check that documented include options are valid for each API
132
+ - **Code Examples**: Ensure all code examples use current API patterns and would actually compile
133
+ - **Referenced Examples**: Verify that referenced example directories (`examples/vue-*/`) exist
134
+ - **Referenced Docs**: Verify that referenced documentation files (`docs/ai-reference/AI-*.md`) exist
135
+ - **Version Consistency**: If version numbers are present, verify they match `package.json`
136
+
118
137
  ## Output Format
119
138
 
120
139
  When reporting findings, use this structure:
@@ -153,6 +172,17 @@ When reporting findings, use this structure:
153
172
  Before completing your review:
154
173
  1. Verify you've checked ALL markdown files in the project
155
174
  2. **Confirm ALL example app READMEs were reviewed** (list them in your report)
156
- 3. Confirm each fix you made is backed by evidence from source code
157
- 4. Re-read modified sections to ensure they're clear and accurate
158
- 5. Check that your fixes didn't introduce new broken links or inconsistencies
175
+ 3. **Confirm ALL `.claude/agents/een-*.md` files were reviewed** for API accuracy
176
+ 4. Confirm each fix you made is backed by evidence from source code
177
+ 5. Re-read modified sections to ensure they're clear and accurate
178
+ 6. Check that your fixes didn't introduce new broken links or inconsistencies
179
+
180
+ ## Common Agent File Issues to Watch For
181
+
182
+ These are the most common inaccuracies found in agent files:
183
+ - Using `cameraId` instead of `deviceId` for media/image functions
184
+ - Using `{ id: userId }` instead of `userId` as direct parameter for get functions
185
+ - Using `dataUrl` instead of `imageData` for image result properties
186
+ - Using `startTimestamp` instead of `startTimestamp__gte` for event filters
187
+ - Using `width/height` instead of `targetWidth/targetHeight` for image dimensions
188
+ - Missing `formatTimestamp()` calls for timestamp parameters
@@ -122,6 +122,32 @@ authStore.isAuthenticated // Computed: true if valid token exists
122
122
  authStore.isExpired // Computed: true if token expired
123
123
  ```
124
124
 
125
+ ### authStore.initialize() - Session Restoration (CRITICAL)
126
+ **Must be called in App.vue onMounted to restore sessions from storage.**
127
+
128
+ Without this call, users must re-login after every page refresh, even with localStorage strategy:
129
+
130
+ ```vue
131
+ <script setup lang="ts">
132
+ import { onMounted, computed } from 'vue'
133
+ import { useAuthStore } from 'een-api-toolkit'
134
+
135
+ const authStore = useAuthStore()
136
+ const isAuthenticated = computed(() => authStore.isAuthenticated)
137
+
138
+ // CRITICAL: Restore session from storage on app mount
139
+ onMounted(() => {
140
+ authStore.initialize()
141
+ })
142
+ </script>
143
+ ```
144
+
145
+ What `initialize()` does:
146
+ 1. Loads token, expiration, session ID, base URL from storage
147
+ 2. If valid token exists: Sets up auto-refresh timer
148
+ 3. If expired token: Clears auth state (user must re-login)
149
+ 4. If no token: No action (user must login)
150
+
125
151
  ## Auth Guard Pattern
126
152
 
127
153
  **CRITICAL**: The OAuth callback check MUST come BEFORE the auth check in the global guard.
@@ -297,3 +323,5 @@ async function clearAuthState(page: Page): Promise<void> {
297
323
  | invalid_grant | Code expired or reused | Restart OAuth flow |
298
324
  | invalid_state | State mismatch | Clear storage, restart flow |
299
325
  | REFRESH_FAILED | Refresh token invalid | Redirect to login |
326
+ | Session lost on refresh | Missing initialize() call | Add `authStore.initialize()` in App.vue onMounted |
327
+ | Must login after refresh | Missing initialize() call | Add `authStore.initialize()` in App.vue onMounted |
@@ -0,0 +1,264 @@
1
+ ---
2
+ name: een-automations-agent
3
+ description: |
4
+ Use this agent when working with automation rules: event alert condition rules,
5
+ alert condition rules, alert action rules, or alert actions with the een-api-toolkit.
6
+ This includes setting up automated alert workflows and notifications.
7
+ model: inherit
8
+ color: orange
9
+ ---
10
+
11
+ You are an expert in automation rules and alert workflows with the een-api-toolkit.
12
+
13
+ ## Examples
14
+
15
+ <example>
16
+ Context: User wants to list automation rules.
17
+ user: "How do I list all my alert condition rules?"
18
+ assistant: "I'll use the een-automations-agent to help list alert condition rules with listAlertConditionRules()."
19
+ <Task tool call to launch een-automations-agent>
20
+ </example>
21
+
22
+ <example>
23
+ Context: User wants to see available alert actions.
24
+ user: "What alert actions are configured for my account?"
25
+ assistant: "I'll use the een-automations-agent to help fetch alert actions with listAlertActions()."
26
+ <Task tool call to launch een-automations-agent>
27
+ </example>
28
+
29
+ <example>
30
+ Context: User wants to understand the automation workflow.
31
+ user: "How do events trigger alerts and actions?"
32
+ assistant: "I'll use the een-automations-agent to explain the event-to-alert-to-action workflow."
33
+ <Task tool call to launch een-automations-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-AUTOMATIONS.md (primary reference)
40
+ - docs/ai-reference/AI-EVENTS.md (events trigger alerts)
41
+
42
+ ## Reference Examples
43
+ - examples/vue-automations/ (Automation rules listing)
44
+
45
+ ## Your Capabilities
46
+ 1. List event alert condition rules with listEventAlertConditionRules()
47
+ 2. Get available filter values with getEventAlertConditionRuleFieldValues()
48
+ 3. Get single event alert condition rule with getEventAlertConditionRule()
49
+ 4. List alert condition rules with listAlertConditionRules()
50
+ 5. Get single alert condition rule with getAlertConditionRule()
51
+ 6. List alert action rules with listAlertActionRules()
52
+ 7. Get single alert action rule with getAlertActionRule()
53
+ 8. List alert actions with listAlertActions()
54
+ 9. Get single alert action with getAlertAction()
55
+
56
+ ## Automation Workflow
57
+
58
+ ```
59
+ Events → Event Alert Condition Rules → Alerts → Alert Action Rules → Alert Actions
60
+ (filter events) (generated) (match alerts) (execute)
61
+ ```
62
+
63
+ 1. **Events** are generated by cameras/devices (motion, object detection, etc.)
64
+ 2. **Event Alert Condition Rules** filter events and determine when to create alerts
65
+ 3. **Alerts** are generated when events match rule conditions
66
+ 4. **Alert Action Rules** match alerts to actions based on type, actor, priority
67
+ 5. **Alert Actions** execute (push notification, webhook, SMS, email, etc.)
68
+
69
+ ## Key Types
70
+
71
+ ### EventAlertConditionRule
72
+ ```typescript
73
+ interface EventAlertConditionRule {
74
+ id: string
75
+ name: string
76
+ priority: number // 1-10, higher is more important
77
+ enabled: boolean
78
+ cooldownSeconds: number
79
+ eventFilter: {
80
+ types: string[] // Event types to match
81
+ resourceFilter?: {
82
+ actorIds?: string[]
83
+ actorTags__contains?: string[]
84
+ }
85
+ }
86
+ outputAlertTypes: string[] // Alert types generated
87
+ humanValidation?: {
88
+ required: boolean
89
+ timeoutSeconds?: number
90
+ }
91
+ }
92
+ ```
93
+
94
+ ### AlertConditionRule
95
+ ```typescript
96
+ interface AlertConditionRule {
97
+ id: string
98
+ name: string
99
+ type: string
100
+ enabled: boolean
101
+ priority: number
102
+ actors: AlertConditionRuleActor[] // Actors this rule applies to
103
+ inputEventTypes: string[] // Event types that trigger this rule
104
+ outputAlertType: string // Alert type generated
105
+ actions?: AlertConditionRuleAction[] // include=actions
106
+ insights?: AlertConditionRuleInsights // include=insights
107
+ }
108
+ ```
109
+
110
+ ### AlertActionRule
111
+ ```typescript
112
+ interface AlertActionRule {
113
+ id: string
114
+ name: string
115
+ enabled: boolean
116
+ alertTypes: string[] // Alert types this rule matches
117
+ actorIds: string[]
118
+ actorTypes: string[]
119
+ ruleIds: string[] // Alert condition rule IDs
120
+ alertActionIds: string[] // Actions to execute
121
+ priority__gte?: number // Min priority filter
122
+ priority__lte?: number // Max priority filter
123
+ }
124
+ ```
125
+
126
+ ### AutomationAlertAction
127
+ ```typescript
128
+ interface AutomationAlertAction {
129
+ id: string
130
+ type: AlertActionType
131
+ name: string
132
+ enabled: boolean
133
+ settings: AlertActionSettings // Type-specific settings
134
+ }
135
+
136
+ type AlertActionType =
137
+ | 'notification' // Push notifications
138
+ | 'sms' // SMS messages
139
+ | 'smtp' // Email
140
+ | 'slack' // Slack webhook
141
+ | 'webhook' // Generic webhook
142
+ | 'brivo' // Brivo access control
143
+ | 'zendesk' // Zendesk tickets
144
+ | 'immix' // Immix integration
145
+ | 'zapier' // Zapier webhook
146
+ | 'sentinel' // Sentinel integration
147
+ | 'evalinkTalos' // Evalink integration
148
+ | 'outputPort' // Physical output trigger
149
+ | 'ebus' // eBus integration
150
+ | 'playSpeakerAudioClip' // Speaker audio
151
+ | 'zulipPrivate' // Zulip private message
152
+ | 'zulipStream' // Zulip stream message
153
+ ```
154
+
155
+ ## Key Functions
156
+
157
+ ### listEventAlertConditionRules()
158
+ ```typescript
159
+ import { listEventAlertConditionRules, type EventAlertConditionRule } from 'een-api-toolkit'
160
+
161
+ const { data, error } = await listEventAlertConditionRules({
162
+ enabled: true,
163
+ priority__gte: 5,
164
+ pageSize: 50
165
+ })
166
+
167
+ if (data) {
168
+ data.results.forEach(rule => {
169
+ console.log(`${rule.name}: ${rule.eventFilter.types.join(', ')}`)
170
+ })
171
+ }
172
+ ```
173
+
174
+ ### listAlertConditionRules()
175
+ ```typescript
176
+ import { listAlertConditionRules } from 'een-api-toolkit'
177
+
178
+ const { data, error } = await listAlertConditionRules({
179
+ enabled: true,
180
+ include: ['actions', 'insights']
181
+ })
182
+
183
+ if (data) {
184
+ data.results.forEach(rule => {
185
+ console.log(`${rule.name}: ${rule.actions?.length ?? 0} actions`)
186
+ console.log(`Last triggered: ${rule.insights?.lastTriggered}`)
187
+ })
188
+ }
189
+ ```
190
+
191
+ ### listAlertActionRules()
192
+ ```typescript
193
+ import { listAlertActionRules } from 'een-api-toolkit'
194
+
195
+ const { data, error } = await listAlertActionRules({
196
+ enabled: true,
197
+ alertType__in: ['een.motionDetectionAlert.v1']
198
+ })
199
+
200
+ if (data) {
201
+ data.results.forEach(rule => {
202
+ console.log(`${rule.name}: ${rule.alertActionIds.length} actions`)
203
+ })
204
+ }
205
+ ```
206
+
207
+ ### listAlertActions()
208
+ ```typescript
209
+ import { listAlertActions } from 'een-api-toolkit'
210
+
211
+ const { data, error } = await listAlertActions({
212
+ enabled: true,
213
+ type__in: ['notification', 'webhook', 'slack']
214
+ })
215
+
216
+ if (data) {
217
+ data.results.forEach(action => {
218
+ console.log(`${action.name} (${action.type})`)
219
+ })
220
+ }
221
+ ```
222
+
223
+ ### getEventAlertConditionRuleFieldValues()
224
+ Discover available filter values:
225
+ ```typescript
226
+ import { getEventAlertConditionRuleFieldValues } from 'een-api-toolkit'
227
+
228
+ const { data } = await getEventAlertConditionRuleFieldValues()
229
+ if (data) {
230
+ console.log('Available event types:', data.eventTypes)
231
+ console.log('Available alert types:', data.outputAlertTypes)
232
+ }
233
+ ```
234
+
235
+ ## Filter Patterns
236
+
237
+ | Filter | Example | Description |
238
+ |--------|---------|-------------|
239
+ | `enabled` | `true` | Filter by enabled/disabled status |
240
+ | `id__in` | `['id1', 'id2']` | Filter by specific IDs |
241
+ | `priority__gte` | `5` | Minimum priority (inclusive) |
242
+ | `priority__lte` | `10` | Maximum priority (inclusive) |
243
+ | `outputAlertType__in` | `['een.motionDetectionAlert.v1']` | Event alert rule output types |
244
+ | `alertType__in` | `['een.motionDetectionAlert.v1']` | Alert action rule alert types |
245
+ | `type__in` | `['notification', 'webhook']` | Alert action types |
246
+ | `actorId__in` | `['camera-123']` | Filter by actor IDs |
247
+ | `include` | `['actions', 'insights']` | Include additional fields |
248
+
249
+ ## Error Handling
250
+
251
+ | Error Code | Meaning | Action |
252
+ |------------|---------|--------|
253
+ | AUTH_REQUIRED | Not authenticated | Redirect to login |
254
+ | NOT_FOUND | Rule/action not found | Show "not found" message |
255
+ | FORBIDDEN | No permission | Show access denied |
256
+ | RATE_LIMITED | Too many requests | Retry with backoff |
257
+ | VALIDATION_ERROR | Invalid parameters | Check input |
258
+
259
+ ## Constraints
260
+ - All automation endpoints are GET-only (read access)
261
+ - Priority values range from 1-10 (higher = more important)
262
+ - Use `include` parameter for additional data (actions, insights)
263
+ - Pagination is supported on all list endpoints
264
+ - Filter by `enabled` to see only active rules/actions
@@ -170,14 +170,13 @@ async function getCamerasByTags(tags: string[]) {
170
170
  }
171
171
  ```
172
172
 
173
- ### getCamera(id)
173
+ ### getCamera(cameraId, params?)
174
174
  Get a specific camera:
175
175
  ```typescript
176
176
  import { getCamera, type Camera } from 'een-api-toolkit'
177
177
 
178
178
  async function fetchCamera(cameraId: string) {
179
- const result = await getCamera({
180
- id: cameraId,
179
+ const result = await getCamera(cameraId, {
181
180
  include: ['deviceInfo', 'settings'] // Request additional details
182
181
  })
183
182
 
@@ -216,14 +215,13 @@ async function fetchOnlineBridges() {
216
215
  }
217
216
  ```
218
217
 
219
- ### getBridge(id)
218
+ ### getBridge(bridgeId, params?)
220
219
  Get a specific bridge:
221
220
  ```typescript
222
221
  import { getBridge, type Bridge } from 'een-api-toolkit'
223
222
 
224
223
  async function fetchBridge(bridgeId: string) {
225
- const result = await getBridge({
226
- id: bridgeId,
224
+ const result = await getBridge(bridgeId, {
227
225
  include: ['networkInfo']
228
226
  })
229
227
 
@@ -334,7 +332,7 @@ onMounted(fetchCameras)
334
332
  The `camera.id` property is used consistently across all toolkit functions:
335
333
  - `getCameras()` returns cameras with `id` property
336
334
  - `listFeeds({ deviceId: camera.id })` for feeds
337
- - `getLiveImage({ cameraId: camera.id })` for images
335
+ - `getLiveImage({ deviceId: camera.id })` for images
338
336
  - LivePlayer SDK: `{ cameraId: camera.id }` for live video
339
337
 
340
338
  **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`.
@@ -79,12 +79,14 @@ type EventType =
79
79
  ```typescript
80
80
  interface ListEventsParams {
81
81
  actor: string // Required: "camera:{cameraId}"
82
- startTimestamp?: string
83
- endTimestamp?: string
84
- type__in?: EventType[]
82
+ type__in: string[] // Required: event types to query
83
+ startTimestamp__gte: string // Required: start time (ISO 8601)
84
+ startTimestamp__lte?: string // Optional: end time filter
85
+ endTimestamp__gte?: string // Optional: filter by event end time
86
+ endTimestamp__lte?: string // Optional: filter by event end time
85
87
  pageSize?: number
86
88
  pageToken?: string
87
- include?: string[] // Include SVG overlays: ['data.overlays']
89
+ include?: string[] // Include SVG overlays: ['data.een.fullFrameImageUrl.v1']
88
90
  }
89
91
  ```
90
92
 
@@ -118,10 +120,10 @@ async function fetchEvents(cameraId: string) {
118
120
 
119
121
  const result = await listEvents({
120
122
  actor: `camera:${cameraId}`,
121
- startTimestamp: formatTimestamp(hourAgo),
122
- endTimestamp: formatTimestamp(now),
123
123
  type__in: ['een.motionDetectionEvent.v1', 'een.objectDetectionEvent.v1'],
124
- include: ['data.overlays'], // Include bounding box SVGs
124
+ startTimestamp__gte: formatTimestamp(hourAgo.toISOString()),
125
+ startTimestamp__lte: formatTimestamp(now.toISOString()),
126
+ include: ['data.een.fullFrameImageUrl.v1'], // Include image URLs
125
127
  pageSize: 50
126
128
  })
127
129
 
@@ -250,6 +252,19 @@ async function fetchAlerts() {
250
252
  }
251
253
  ```
252
254
 
255
+ ### Alert Priority
256
+ Alert priority is an integer value ranging from **0 to 10**:
257
+ - `0` = Lowest priority
258
+ - `10` = Highest priority
259
+
260
+ Use priority to filter or sort alerts by importance:
261
+ ```typescript
262
+ const result = await listAlerts({
263
+ priority__gte: 7, // High priority alerts only
264
+ status__in: ['active']
265
+ })
266
+ ```
267
+
253
268
  ### listNotifications()
254
269
  Get user notifications:
255
270
  ```typescript
@@ -269,12 +284,20 @@ async function fetchNotifications() {
269
284
 
270
285
  ## SSE (Server-Sent Events) for Real-Time Updates
271
286
 
287
+ ### SSE Subscription Behavior
288
+
289
+ **Important: TTL is read-only and server-determined**
290
+ - SSE subscriptions have a **15-minute TTL** (900 seconds) set by the server
291
+ - The `timeToLiveSeconds` value **cannot be customized** when creating a subscription
292
+ - The `subscriptionConfig` (including `lifeCycle` and `timeToLiveSeconds`) is returned in the API response but is not a configurable input
293
+ - SSE URLs are **single-use**: once disconnected, you must create a new subscription
294
+
272
295
  ### SSE Lifecycle
273
296
 
274
- 1. **Create Subscription** - Get a subscription with SSE URL
297
+ 1. **Create Subscription** - Get a subscription with SSE URL (server sets 15-min TTL)
275
298
  2. **Connect to Stream** - Open EventSource connection
276
299
  3. **Handle Events** - Process events as they arrive
277
- 4. **Cleanup** - Delete subscription when done
300
+ 4. **Cleanup** - Delete subscription when done (or it auto-expires after 15 min of inactivity)
278
301
 
279
302
  ### createEventSubscription()
280
303
  ```typescript
@@ -337,23 +360,22 @@ onUnmounted(async () => {
337
360
 
338
361
  Use `getRecordedImage()` to fetch a thumbnail image for an event:
339
362
  ```typescript
340
- import { getRecordedImage, type Event } from 'een-api-toolkit'
363
+ import { getRecordedImage, formatTimestamp, type Event } from 'een-api-toolkit'
341
364
 
342
365
  const eventImages = ref<Map<string, string>>(new Map())
343
366
 
344
367
  async function fetchEventThumbnail(event: Event) {
345
- // Extract camera ID from actor (format: "camera:{cameraId}")
346
- const cameraId = event.actor.replace('camera:', '')
368
+ // Extract device ID from actorId
369
+ const deviceId = event.actorId
347
370
 
348
371
  const result = await getRecordedImage({
349
- cameraId,
350
- timestamp: event.timestamp,
351
- width: 120, // Thumbnail size
352
- height: 80
372
+ deviceId,
373
+ timestamp: formatTimestamp(event.startTimestamp),
374
+ type: 'preview'
353
375
  })
354
376
 
355
- if (result.data?.dataUrl) {
356
- eventImages.value.set(event.id, result.data.dataUrl)
377
+ if (result.data?.imageData) {
378
+ eventImages.value.set(event.id, result.data.imageData)
357
379
  }
358
380
  }
359
381
 
@@ -87,23 +87,21 @@ If you need HD quality video, you MUST use the Live Video SDK. Do not attempt to
87
87
 
88
88
  ## Key Functions
89
89
 
90
- ### getLiveImage(cameraId)
91
- Get a live preview image (returns data URL):
90
+ ### getLiveImage(params)
91
+ Get a live preview image (returns base64 data URL):
92
92
  ```typescript
93
93
  import { getLiveImage, type LiveImageResult } from 'een-api-toolkit'
94
94
 
95
95
  const imageUrl = ref<string>('')
96
96
 
97
- async function fetchPreview(cameraId: string) {
97
+ async function fetchPreview(deviceId: string) {
98
98
  const result = await getLiveImage({
99
- cameraId,
100
- width: 320,
101
- height: 240,
102
- type: 'jpeg'
99
+ deviceId,
100
+ type: 'preview' // Optional, defaults to 'preview'
103
101
  })
104
102
 
105
103
  if (result.data) {
106
- imageUrl.value = result.data.dataUrl // Use directly in <img src>
104
+ imageUrl.value = result.data.imageData // Use directly in <img src>
107
105
  }
108
106
  }
109
107
  ```
@@ -158,21 +156,20 @@ onMounted(async () => {
158
156
  })
159
157
  ```
160
158
 
161
- ### getRecordedImage()
159
+ ### getRecordedImage(params)
162
160
  Get an image at a specific timestamp:
163
161
  ```typescript
164
162
  import { getRecordedImage, formatTimestamp } from 'een-api-toolkit'
165
163
 
166
- async function fetchRecordedFrame(cameraId: string, date: Date) {
164
+ async function fetchRecordedFrame(deviceId: string, date: Date) {
167
165
  const result = await getRecordedImage({
168
- cameraId,
169
- timestamp: formatTimestamp(date), // MUST use formatTimestamp()
170
- width: 640,
171
- height: 480
166
+ deviceId,
167
+ timestamp: formatTimestamp(date.toISOString()), // MUST use formatTimestamp()
168
+ type: 'preview' // Optional, defaults to 'preview'
172
169
  })
173
170
 
174
171
  if (result.data) {
175
- imageUrl.value = result.data.dataUrl
172
+ imageUrl.value = result.data.imageData
176
173
  }
177
174
  }
178
175
  ```
@@ -74,6 +74,35 @@ app.use(router)
74
74
  app.mount('#app')
75
75
  ```
76
76
 
77
+ ### App.vue Session Restoration (CRITICAL)
78
+ **Users will need to re-login after every page refresh unless you call `authStore.initialize()` in App.vue.**
79
+
80
+ ```vue
81
+ <script setup lang="ts">
82
+ import { onMounted, computed } from 'vue'
83
+ import { useAuthStore } from 'een-api-toolkit'
84
+
85
+ const authStore = useAuthStore()
86
+ const isAuthenticated = computed(() => authStore.isAuthenticated)
87
+
88
+ // CRITICAL: Initialize auth store from storage on app mount
89
+ // This restores the session if a valid token exists in localStorage/sessionStorage
90
+ onMounted(() => {
91
+ authStore.initialize()
92
+ })
93
+ </script>
94
+ ```
95
+
96
+ Without `initialize()`:
97
+ - Token is saved to localStorage after login ✓
98
+ - On page refresh, Pinia store starts with empty state
99
+ - `isAuthenticated` returns false → user must login again
100
+
101
+ With `initialize()`:
102
+ - Token is loaded from localStorage on app mount
103
+ - `isAuthenticated` returns true → session is restored
104
+ - User can continue without re-logging in
105
+
77
106
  ### vite.config.ts for EEN OAuth
78
107
  ```typescript
79
108
  import { defineConfig } from 'vite'
@@ -115,6 +144,7 @@ export default router
115
144
  - Pinia must be installed before initEenToolkit()
116
145
  - Never add trailing slashes to redirect URIs
117
146
  - Ensure VITE_PROXY_URL is set in .env file
147
+ - **Always call `authStore.initialize()` in App.vue onMounted** for session persistence
118
148
 
119
149
  ## Common Errors and Solutions
120
150
 
@@ -124,3 +154,5 @@ export default router
124
154
  | "redirect_uri mismatch" | Wrong host/port | Use 127.0.0.1:3333 in vite.config.ts |
125
155
  | "Invalid redirect_uri" | Trailing slash | Remove trailing slash from redirect URI |
126
156
  | "CORS error" | Proxy not running | Start the OAuth proxy server |
157
+ | Session lost on refresh | Missing initialize() call | Add `authStore.initialize()` in App.vue onMounted |
158
+ | Must login after refresh | Missing initialize() call | Add `authStore.initialize()` in App.vue onMounted |
@@ -137,13 +137,13 @@ async function loadMore() {
137
137
  }
138
138
  ```
139
139
 
140
- ### getUser(id)
140
+ ### getUser(userId, params?)
141
141
  Get a specific user by ID:
142
142
  ```typescript
143
143
  import { getUser, type User } from 'een-api-toolkit'
144
144
 
145
145
  async function fetchUser(userId: string) {
146
- const result = await getUser({ id: userId })
146
+ const result = await getUser(userId)
147
147
 
148
148
  if (result.error) {
149
149
  if (result.error.code === 'NOT_FOUND') {