@apocaliss92/scrypted-advanced-notifier 4.8.36 → 4.8.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/CHANGELOG.md +4 -0
- package/README.md +29 -2
- package/dist/plugin.zip +0 -0
- package/docs/INTERFACE_DESCRIPTORS_MIGRATION.md +184 -0
- package/package.json +26 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
<details>
|
|
2
2
|
<summary>Changelog</summary>
|
|
3
3
|
|
|
4
|
+
### 4.8.37
|
|
5
|
+
|
|
6
|
+
- **On-generated sequences**: new sequence hook for Detection, Occupancy and Timelapse rules. Runs when all rule artifacts (video, gif, image) have been generated. Configurable per rule under "On-generated sequences". Script actions in the sequence receive a `payload` object (e.g. `variables.payload` in Scrypted scripts) with `rule`, `videoUrl`, `gifUrl`, `imageUrl` (and for timelapse also `videoPath`, `imagePath`). Any sequence can now receive an optional payload and forward it to linked scripts.
|
|
7
|
+
|
|
4
8
|
### 4.8.36
|
|
5
9
|
|
|
6
10
|
- Setting added on every rule to allow the selection of an additional path to save artifacts, with filename {deviceId}_{ruleName}_{triggerTime}.{ext}
|
package/README.md
CHANGED
|
@@ -190,8 +190,35 @@ Audio rules will monitor the audio received by the camera
|
|
|
190
190
|
|
|
191
191
|
## Sequences
|
|
192
192
|
|
|
193
|
-
In the sequences section, on the plugin page, you will be able to define a custom sequence with various steps.
|
|
194
|
-
|
|
193
|
+
In the sequences section, on the plugin page, you will be able to define a custom sequence with various steps. Each sequence can contain actions such as: Wait, Script, PTZ preset, Switch on/off, Lock, Entry open/close.
|
|
194
|
+
|
|
195
|
+
Sequences can be attached to rules at different moments:
|
|
196
|
+
|
|
197
|
+
- **On-activation sequences** (rules with activation type other than Always): run when the rule becomes active.
|
|
198
|
+
- **On-deactivation sequences**: run when the rule becomes inactive.
|
|
199
|
+
- **On-trigger sequences**: run when the rule is triggered (e.g. detection matched, occupancy change).
|
|
200
|
+
- **On-reset sequences**: run when the rule is reset (e.g. after the trigger window ends).
|
|
201
|
+
- **On-generated sequences**: run when all artifacts for that rule have been generated (video clip, gif, image). Available for Detection, Occupancy and Timelapse rules.
|
|
202
|
+
|
|
203
|
+
When a sequence is run with a **payload** (e.g. On-generated), Script actions receive it as run variables. In Scrypted scripts, access it via `variables.payload`.
|
|
204
|
+
|
|
205
|
+
### Payload for On-generated sequences
|
|
206
|
+
|
|
207
|
+
For **On-generated sequences**, the payload passed to scripts has the following shape:
|
|
208
|
+
|
|
209
|
+
- **Detection and Occupancy rules**: `{ rule, videoUrl?, gifUrl?, imageUrl }`
|
|
210
|
+
- `rule`: the rule object (name, notifiers, settings, etc.).
|
|
211
|
+
- `videoUrl`: URL of the generated video clip (if clip type is MP4).
|
|
212
|
+
- `gifUrl`: URL of the generated GIF (if clip type is GIF).
|
|
213
|
+
- `imageUrl`: URL of the stored snapshot used for the notification.
|
|
214
|
+
|
|
215
|
+
- **Timelapse rules**: `{ rule, videoUrl, imageUrl, videoPath?, imagePath? }`
|
|
216
|
+
- `rule`: the timelapse rule object.
|
|
217
|
+
- `videoUrl`: URL to the generated timelapse video.
|
|
218
|
+
- `imageUrl`: URL to the generated thumbnail image.
|
|
219
|
+
- `videoPath`, `imagePath`: filesystem paths where the video and image were saved (when available).
|
|
220
|
+
|
|
221
|
+
(Initially only a few action types will be available, ask for more if you wish something more specific)
|
|
195
222
|
|
|
196
223
|
## Stored images
|
|
197
224
|
|
package/dist/plugin.zip
CHANGED
|
Binary file
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# Migrazione da Webhook HTTP a Socket SDK con interfaceDescriptors
|
|
2
|
+
|
|
3
|
+
Piano per esporre i metodi Events App via **interfaceDescriptors** (come [@scrypted/llm](https://github.com/scryptedapp/llm)), eliminando le chiamate REST e usando solo la socket SDK.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Come funziona interfaceDescriptors (LLM plugin)
|
|
8
|
+
|
|
9
|
+
Dal [package.json dell'LLM](https://github.com/scryptedapp/llm/blob/main/package.json):
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"scrypted": {
|
|
14
|
+
"interfaces": ["DeviceProvider", "UserDatabase", ...],
|
|
15
|
+
"interfaceDescriptors": {
|
|
16
|
+
"UserDatabase": {
|
|
17
|
+
"name": "UserDatabase",
|
|
18
|
+
"methods": ["openDatabase"],
|
|
19
|
+
"properties": []
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
- **interfaceDescriptors** dichiara interfacce custom con metodi e proprietà
|
|
27
|
+
- Il server Scrypted usa questi descrittori per esporre i metodi via RPC sulla socket
|
|
28
|
+
- Il client può chiamare `device.openDatabase()` invece di fare HTTP
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 2. Metodi Events App da esporre (handleEventsAppRequest)
|
|
33
|
+
|
|
34
|
+
| apimethod | payload | Note |
|
|
35
|
+
|-----------|---------|------|
|
|
36
|
+
| GetConfigs | — | |
|
|
37
|
+
| GetCamerasStatus | — | |
|
|
38
|
+
| GetEvents | fromDate, tillDate, limit, offset, sources, cameras, detectionClasses, eventSource, filter, groupingRange | |
|
|
39
|
+
| GetVideoclips | fromDate, tillDate, limit, offset, cameras, detectionClasses | |
|
|
40
|
+
| GetCameraDayData | deviceId, day | |
|
|
41
|
+
| GetClusteredDayData | deviceId, days, bucketMs, enabledClasses, classFilter | |
|
|
42
|
+
| GetClusterEvents | clusterId, deviceId, startMs, endMs | |
|
|
43
|
+
| GetArtifacts | deviceId, day | |
|
|
44
|
+
| GetLatestRuleArtifacts | deviceId, limit | |
|
|
45
|
+
| RemoteLog | level, message | |
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 3. Modifiche al plugin advanced-notifier
|
|
50
|
+
|
|
51
|
+
### 3.1 package.json — aggiungere interfaceDescriptors
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"scrypted": {
|
|
56
|
+
"interfaces": ["Settings", "DeviceProvider", "MixinProvider", "HttpRequestHandler", "Videoclips", "LauncherApplication", "PushHandler"],
|
|
57
|
+
"interfaceDescriptors": {
|
|
58
|
+
"EventsAppApi": {
|
|
59
|
+
"name": "EventsAppApi",
|
|
60
|
+
"methods": [
|
|
61
|
+
"getConfigs",
|
|
62
|
+
"getCamerasStatus",
|
|
63
|
+
"getEvents",
|
|
64
|
+
"getVideoclips",
|
|
65
|
+
"getCameraDayData",
|
|
66
|
+
"getClusteredDayData",
|
|
67
|
+
"getClusterEvents",
|
|
68
|
+
"getArtifacts",
|
|
69
|
+
"getLatestRuleArtifacts",
|
|
70
|
+
"remoteLog"
|
|
71
|
+
],
|
|
72
|
+
"properties": []
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 3.2 utils.ts — costante interfaccia
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
export const EVENTS_APP_API_INTERFACE = "EventsAppApi";
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 3.3 main.ts — aggiungere interfaccia al data fetcher
|
|
86
|
+
|
|
87
|
+
In `onDeviceDiscovered` per DATA_FETCHER_NATIVE_ID:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
interfaces: [
|
|
91
|
+
ScryptedInterface.VideoClips,
|
|
92
|
+
ScryptedInterface.EventRecorder,
|
|
93
|
+
ScryptedInterface.Settings,
|
|
94
|
+
EVENTS_APP_API_INTERFACE, // <-- aggiungere
|
|
95
|
+
],
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 3.4 dataFetcher.ts — implementare EventsAppApi
|
|
99
|
+
|
|
100
|
+
La classe `AdvancedNotifierDataFetcher` deve implementare i metodi pubblici che mappano 1:1 con gli apimethod. Esempio:
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
// EventsAppApi interface
|
|
104
|
+
async getConfigs(): Promise<{ cameras: ...; enabledDetectionSources: string[] }> {
|
|
105
|
+
const { statusCode, body } = await this.handleEventsAppRequest('GetConfigs', {});
|
|
106
|
+
if (statusCode !== 200) throw new Error(JSON.stringify(body));
|
|
107
|
+
return body as any;
|
|
108
|
+
}
|
|
109
|
+
async getCamerasStatus(): Promise<CamerasStatusResponse> { ... }
|
|
110
|
+
async getEvents(payload: GetEventsPayload): Promise<GetEventsResponse> { ... }
|
|
111
|
+
// ... etc
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Oppure, più pulito: estrarre la logica da `handleEventsAppRequest` in metodi dedicati e far sì che `handleEventsAppRequest` li chiami, così si evita duplicazione.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 4. Come il server Scrypted gestisce interfaceDescriptors
|
|
119
|
+
|
|
120
|
+
Il server Scrypted (koush/scrypted) legge `interfaceDescriptors` dal `package.json` del plugin. Quando un device dichiara un'interfaccia in `interfaces`, il server:
|
|
121
|
+
|
|
122
|
+
1. Verifica che l'interfaccia sia in `interfaceDescriptors` (per interfacce custom)
|
|
123
|
+
2. Espone i metodi via RPC sulla socket Engine.IO
|
|
124
|
+
3. Il client `@scrypted/client` può chiamare `device.getConfigs()` e la chiamata viene serializzata e inviata via socket
|
|
125
|
+
|
|
126
|
+
Non serve modificare il server: il supporto è già presente. Il client deve solo usare `client.systemManager.getDeviceById(deviceId)` e chiamare i metodi sull'oggetto restituito.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## 5. Modifiche al client (camstack / scrypted-an-frontend)
|
|
131
|
+
|
|
132
|
+
### 5.1 Trovare il device Events App
|
|
133
|
+
|
|
134
|
+
Il device "Advanced notifier data fetcher" ha tipo `API` e implementa `EventsAppApi`. Per ottenere il suo ID:
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
const state = client.systemManager.getSystemState();
|
|
138
|
+
const eventsAppDeviceId = Object.entries(state).find(
|
|
139
|
+
([_, d]) => (d as any)?.interfaces?.includes?.('EventsAppApi')
|
|
140
|
+
)?.[0];
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Oppure cercare per nome/tipo se lo stato lo espone.
|
|
144
|
+
|
|
145
|
+
### 5.2 Sostituire fetch con chiamate SDK
|
|
146
|
+
|
|
147
|
+
**Prima (HTTP):**
|
|
148
|
+
```ts
|
|
149
|
+
const res = await fetch(`${baseUrl}/eventsApp`, {
|
|
150
|
+
method: 'POST',
|
|
151
|
+
body: JSON.stringify({ apimethod: 'GetClusteredDayData', payload: { deviceId, days, bucketMs } }),
|
|
152
|
+
headers: { 'Content-Type': 'application/json', Authorization: getAuthHeader(auth) },
|
|
153
|
+
});
|
|
154
|
+
const data = await res.json();
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Dopo (Socket):**
|
|
158
|
+
```ts
|
|
159
|
+
const client = await getScryptedClient(auth);
|
|
160
|
+
const device = client.systemManager.getDeviceById(eventsAppDeviceId) as EventsAppApi;
|
|
161
|
+
const data = await device.getClusteredDayData({ deviceId, days, bucketMs });
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### 5.3 Cosa resta su HTTP
|
|
165
|
+
|
|
166
|
+
- **URL di immagini/thumbnail/video**: usati in `<Image src={url} />` e `<Video source={{ uri }} />` — devono restare URL HTTP. Il plugin continua a servire `/eventThumbnail/...`, `/eventImage/...`, `/eventVideoclip/...` via HttpRequestHandler.
|
|
167
|
+
- **Autenticazione**: la socket SDK usa già le credenziali del client (login con username/password). Non serve più Basic auth per le chiamate dati.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## 6. Ordine di implementazione
|
|
172
|
+
|
|
173
|
+
1. **Plugin**: aggiungere `interfaceDescriptors` e `EVENTS_APP_API_INTERFACE`, implementare i metodi su `AdvancedNotifierDataFetcher`
|
|
174
|
+
2. **Mantenere HttpRequestHandler**: per `apimethod` POST a `/eventsApp` — opzionale durante la transizione (fallback)
|
|
175
|
+
3. **Client**: creare `eventsAppSdk.ts` che usa la socket; `eventsAppApi.ts` può passare a usare l'SDK quando il client è connesso
|
|
176
|
+
4. **Rimuovere** le chiamate fetch a `/eventsApp` dal client una volta validato l'SDK
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## 7. Riferimenti
|
|
181
|
+
|
|
182
|
+
- [LLM plugin package.json](https://github.com/scryptedapp/llm/blob/main/package.json) — esempio interfaceDescriptors
|
|
183
|
+
- [Scrypted Developer Docs](https://developer.scrypted.app/) — interfacce e plugin
|
|
184
|
+
- [@scrypted/client](https://www.npmjs.com/package/@scrypted/client) — SDK client con socket
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/apocaliss92/scrypted-advanced-notifier"
|
|
7
7
|
},
|
|
8
|
-
"version": "4.8.
|
|
8
|
+
"version": "4.8.38",
|
|
9
9
|
"scripts": {
|
|
10
10
|
"scrypted-setup-project": "scrypted-setup-project",
|
|
11
11
|
"prescrypted-setup-project": "scrypted-package-json",
|
|
@@ -49,9 +49,31 @@
|
|
|
49
49
|
"HttpRequestHandler",
|
|
50
50
|
"Videoclips",
|
|
51
51
|
"LauncherApplication",
|
|
52
|
-
"PushHandler"
|
|
52
|
+
"PushHandler",
|
|
53
|
+
"EventsAppApi"
|
|
53
54
|
],
|
|
54
|
-
"pluginDependencies": []
|
|
55
|
+
"pluginDependencies": [],
|
|
56
|
+
"interfaceDescriptors": {
|
|
57
|
+
"EventsAppApi": {
|
|
58
|
+
"name": "EventsAppApi",
|
|
59
|
+
"methods": [
|
|
60
|
+
"getConfigs",
|
|
61
|
+
"getCamerasStatus",
|
|
62
|
+
"getEvents",
|
|
63
|
+
"getVideoclips",
|
|
64
|
+
"getCameraDayData",
|
|
65
|
+
"getClusteredDayData",
|
|
66
|
+
"getClusterEvents",
|
|
67
|
+
"getArtifacts",
|
|
68
|
+
"getLatestRuleArtifacts",
|
|
69
|
+
"remoteLog",
|
|
70
|
+
"getAsset",
|
|
71
|
+
"getVideoClipThumbnailData",
|
|
72
|
+
"getVideoClipData"
|
|
73
|
+
],
|
|
74
|
+
"properties": []
|
|
75
|
+
}
|
|
76
|
+
}
|
|
55
77
|
},
|
|
56
78
|
"dependencies": {
|
|
57
79
|
"@scrypted/common": "file:../scrypted/common",
|
|
@@ -69,4 +91,4 @@
|
|
|
69
91
|
"@types/lodash": "^4.17.12",
|
|
70
92
|
"@types/node": "^20.11.0"
|
|
71
93
|
}
|
|
72
|
-
}
|
|
94
|
+
}
|