jw-automator 2.0.0 → 3.1.0
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 +76 -0
- package/README.md +375 -192
- package/docs/ARCHITECTURE.md +342 -0
- package/docs/MIGRATION.md +411 -0
- package/docs/QUICKSTART.md +356 -0
- package/examples/basic-example.js +135 -0
- package/examples/hello-world.js +40 -0
- package/examples/iot-sensor-example.js +149 -0
- package/index.js +7 -0
- package/package.json +53 -19
- package/src/Automator.js +476 -0
- package/src/core/CoreEngine.js +210 -0
- package/src/core/RecurrenceEngine.js +237 -0
- package/src/host/SchedulerHost.js +174 -0
- package/src/storage/FileStorage.js +59 -0
- package/src/storage/MemoryStorage.js +27 -0
- package/.actions.json +0 -1
- package/.jshintrc +0 -16
- package/.vscode/settings.json +0 -6
- package/LICENSE +0 -674
- package/automator.js +0 -696
- package/demo.js +0 -76
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
# Quick Start Guide
|
|
2
|
+
|
|
3
|
+
Get up and running with jw-automator v3 in 5 minutes.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install jw-automator
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Basic Example
|
|
16
|
+
|
|
17
|
+
Create a file `app.js`:
|
|
18
|
+
|
|
19
|
+
```javascript
|
|
20
|
+
const Automator = require('jw-automator');
|
|
21
|
+
|
|
22
|
+
// Create automator with file-based storage
|
|
23
|
+
const automator = new Automator({
|
|
24
|
+
storage: Automator.storage.file('./my-actions.json')
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Register a function
|
|
28
|
+
automator.addFunction('sayHello', function(payload) {
|
|
29
|
+
console.log(`Hello, ${payload.name}!`);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Add a repeating action
|
|
33
|
+
automator.addAction({
|
|
34
|
+
name: 'Greeting',
|
|
35
|
+
cmd: 'sayHello',
|
|
36
|
+
date: new Date(Date.now() + 2000), // Start in 2 seconds
|
|
37
|
+
payload: { name: 'World' },
|
|
38
|
+
repeat: {
|
|
39
|
+
type: 'second',
|
|
40
|
+
interval: 5,
|
|
41
|
+
limit: 3 // Run 3 times then stop
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Listen to events
|
|
46
|
+
automator.on('action', (event) => {
|
|
47
|
+
console.log(`Action executed: ${event.name}`);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Start the scheduler
|
|
51
|
+
automator.start();
|
|
52
|
+
|
|
53
|
+
console.log('Automator started! Greeting will run 3 times.');
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Run it:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
node app.js
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Common Patterns
|
|
65
|
+
|
|
66
|
+
### Daily Task at Specific Time
|
|
67
|
+
|
|
68
|
+
```javascript
|
|
69
|
+
automator.addAction({
|
|
70
|
+
name: 'Daily Backup',
|
|
71
|
+
cmd: 'runBackup',
|
|
72
|
+
date: new Date('2025-05-01T02:00:00'), // 2:00 AM
|
|
73
|
+
repeat: {
|
|
74
|
+
type: 'day',
|
|
75
|
+
interval: 1
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Weekday Task
|
|
81
|
+
|
|
82
|
+
```javascript
|
|
83
|
+
automator.addAction({
|
|
84
|
+
name: 'Weekday Reminder',
|
|
85
|
+
cmd: 'sendReminder',
|
|
86
|
+
date: new Date('2025-05-01T09:00:00'), // 9:00 AM
|
|
87
|
+
repeat: {
|
|
88
|
+
type: 'weekday',
|
|
89
|
+
interval: 1 // Every weekday
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Every N Minutes
|
|
95
|
+
|
|
96
|
+
```javascript
|
|
97
|
+
automator.addAction({
|
|
98
|
+
name: 'Health Check',
|
|
99
|
+
cmd: 'checkHealth',
|
|
100
|
+
date: new Date(),
|
|
101
|
+
repeat: {
|
|
102
|
+
type: 'minute',
|
|
103
|
+
interval: 15 // Every 15 minutes
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Limited Run Count
|
|
109
|
+
|
|
110
|
+
```javascript
|
|
111
|
+
automator.addAction({
|
|
112
|
+
name: 'Startup Sequence',
|
|
113
|
+
cmd: 'initSystem',
|
|
114
|
+
date: new Date(),
|
|
115
|
+
repeat: {
|
|
116
|
+
type: 'second',
|
|
117
|
+
interval: 1,
|
|
118
|
+
limit: 10 // Run exactly 10 times
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### End Date
|
|
124
|
+
|
|
125
|
+
```javascript
|
|
126
|
+
automator.addAction({
|
|
127
|
+
name: 'Summer Sprinklers',
|
|
128
|
+
cmd: 'waterLawn',
|
|
129
|
+
date: new Date('2025-06-01T06:00:00'),
|
|
130
|
+
repeat: {
|
|
131
|
+
type: 'day',
|
|
132
|
+
interval: 1,
|
|
133
|
+
endDate: new Date('2025-09-01T00:00:00') // Stop after summer
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Buffered vs UnBuffered
|
|
141
|
+
|
|
142
|
+
### Buffered (Default) - Catch Up Missed Events
|
|
143
|
+
|
|
144
|
+
```javascript
|
|
145
|
+
automator.addAction({
|
|
146
|
+
name: 'Critical Task',
|
|
147
|
+
cmd: 'criticalTask',
|
|
148
|
+
date: new Date('2025-05-01T10:00:00'),
|
|
149
|
+
unBuffered: false, // Execute even if delayed
|
|
150
|
+
repeat: { type: 'hour', interval: 1 }
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
If the system is offline from 10:00 to 13:00, it will execute the 10:00, 11:00, and 12:00 occurrences when it comes back online.
|
|
155
|
+
|
|
156
|
+
### UnBuffered - Skip Missed Events
|
|
157
|
+
|
|
158
|
+
```javascript
|
|
159
|
+
automator.addAction({
|
|
160
|
+
name: 'Animation Frame',
|
|
161
|
+
cmd: 'updateAnimation',
|
|
162
|
+
date: new Date(),
|
|
163
|
+
unBuffered: true, // Only run if on time
|
|
164
|
+
repeat: { type: 'second', interval: 1 }
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
If the system is delayed, it won't execute missed animation frames.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Simulation
|
|
173
|
+
|
|
174
|
+
Preview what will happen in the future:
|
|
175
|
+
|
|
176
|
+
```javascript
|
|
177
|
+
const tomorrow = new Date();
|
|
178
|
+
tomorrow.setDate(tomorrow.getDate() + 1);
|
|
179
|
+
|
|
180
|
+
const events = automator.getActionsInRange(new Date(), tomorrow);
|
|
181
|
+
|
|
182
|
+
console.log(`${events.length} events scheduled in next 24 hours`);
|
|
183
|
+
|
|
184
|
+
events.slice(0, 5).forEach(event => {
|
|
185
|
+
console.log(`${event.name} at ${event.scheduledTime.toLocaleString()}`);
|
|
186
|
+
});
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Storage Options
|
|
192
|
+
|
|
193
|
+
### File Storage
|
|
194
|
+
|
|
195
|
+
```javascript
|
|
196
|
+
const automator = new Automator({
|
|
197
|
+
storage: Automator.storage.file('./actions.json')
|
|
198
|
+
});
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Memory Storage (No Persistence)
|
|
202
|
+
|
|
203
|
+
```javascript
|
|
204
|
+
const automator = new Automator({
|
|
205
|
+
storage: Automator.storage.memory()
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Custom Storage
|
|
210
|
+
|
|
211
|
+
```javascript
|
|
212
|
+
const automator = new Automator({
|
|
213
|
+
storage: {
|
|
214
|
+
load: function() {
|
|
215
|
+
// Load from database, cloud, etc.
|
|
216
|
+
return { actions: [...] };
|
|
217
|
+
},
|
|
218
|
+
save: function(state) {
|
|
219
|
+
// Save to database, cloud, etc.
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Event Handling
|
|
228
|
+
|
|
229
|
+
```javascript
|
|
230
|
+
// Scheduler ready
|
|
231
|
+
automator.on('ready', () => {
|
|
232
|
+
console.log('Scheduler started');
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// Action executed
|
|
236
|
+
automator.on('action', (event) => {
|
|
237
|
+
console.log('Action:', event.name);
|
|
238
|
+
console.log('Scheduled:', event.scheduledTime);
|
|
239
|
+
console.log('Actual:', event.actualTime);
|
|
240
|
+
console.log('Count:', event.count);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// Action added/updated/removed
|
|
244
|
+
automator.on('update', (event) => {
|
|
245
|
+
console.log('Update:', event.operation, event.actionId);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// Errors
|
|
249
|
+
automator.on('error', (event) => {
|
|
250
|
+
console.error('Error:', event.message);
|
|
251
|
+
});
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Managing Actions
|
|
257
|
+
|
|
258
|
+
### Add
|
|
259
|
+
|
|
260
|
+
```javascript
|
|
261
|
+
const id = automator.addAction({
|
|
262
|
+
name: 'My Task',
|
|
263
|
+
cmd: 'myCommand',
|
|
264
|
+
date: new Date(),
|
|
265
|
+
repeat: { type: 'hour', interval: 1 }
|
|
266
|
+
});
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Update
|
|
270
|
+
|
|
271
|
+
```javascript
|
|
272
|
+
// By ID
|
|
273
|
+
automator.updateActionByID(id, {
|
|
274
|
+
name: 'Updated Task',
|
|
275
|
+
repeat: { type: 'hour', interval: 2 }
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// By name
|
|
279
|
+
automator.updateActionByName('My Task', {
|
|
280
|
+
payload: { updated: true }
|
|
281
|
+
});
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Remove
|
|
285
|
+
|
|
286
|
+
```javascript
|
|
287
|
+
// By ID
|
|
288
|
+
automator.removeActionByID(id);
|
|
289
|
+
|
|
290
|
+
// By name
|
|
291
|
+
automator.removeActionByName('My Task');
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Query
|
|
295
|
+
|
|
296
|
+
```javascript
|
|
297
|
+
// All actions
|
|
298
|
+
const all = automator.getActions();
|
|
299
|
+
|
|
300
|
+
// By name
|
|
301
|
+
const tasks = automator.getActionsByName('My Task');
|
|
302
|
+
|
|
303
|
+
// By ID
|
|
304
|
+
const task = automator.getActionByID(id);
|
|
305
|
+
|
|
306
|
+
// Description
|
|
307
|
+
const desc = automator.describeAction(id);
|
|
308
|
+
console.log(desc);
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## DST Handling
|
|
314
|
+
|
|
315
|
+
For actions that run during daylight saving time transitions:
|
|
316
|
+
|
|
317
|
+
### Fall Back (Repeated Hour)
|
|
318
|
+
|
|
319
|
+
```javascript
|
|
320
|
+
automator.addAction({
|
|
321
|
+
name: 'DST Aware',
|
|
322
|
+
cmd: 'task',
|
|
323
|
+
date: new Date('2025-11-02T01:30:00'), // Falls in repeated hour
|
|
324
|
+
repeat: {
|
|
325
|
+
type: 'day',
|
|
326
|
+
interval: 1,
|
|
327
|
+
dstPolicy: 'once' // Only run the first 1:30 AM
|
|
328
|
+
// or dstPolicy: 'twice' to run both
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Graceful Shutdown
|
|
336
|
+
|
|
337
|
+
```javascript
|
|
338
|
+
process.on('SIGINT', () => {
|
|
339
|
+
console.log('Shutting down...');
|
|
340
|
+
automator.stop(); // Saves state
|
|
341
|
+
process.exit(0);
|
|
342
|
+
});
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## Next Steps
|
|
348
|
+
|
|
349
|
+
- Read the full [README](../README.md)
|
|
350
|
+
- Check out [examples](../examples/)
|
|
351
|
+
- Review [Architecture](./ARCHITECTURE.md)
|
|
352
|
+
- See [Migration Guide](./MIGRATION.md) if upgrading from v1
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
Happy Automating!
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Basic Example - jw-automator v3
|
|
3
|
+
*
|
|
4
|
+
* This example demonstrates the core features of jw-automator.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const Automator = require('../index');
|
|
8
|
+
|
|
9
|
+
// Create automator with file-based persistence
|
|
10
|
+
const automator = new Automator({
|
|
11
|
+
storage: Automator.storage.file('./example-actions.json'),
|
|
12
|
+
autoSave: true,
|
|
13
|
+
saveInterval: 5000
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
// Register command functions
|
|
17
|
+
automator.addFunction('logMessage', function(payload, event) {
|
|
18
|
+
console.log(`[${new Date().toLocaleTimeString()}] ${payload.message}`);
|
|
19
|
+
console.log(` Scheduled: ${event.scheduledTime.toLocaleTimeString()}`);
|
|
20
|
+
console.log(` Execution count: ${event.count + 1}`);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
automator.addFunction('morningRoutine', function(payload) {
|
|
24
|
+
console.log('Good morning! Starting daily routine...');
|
|
25
|
+
console.log('- Check weather');
|
|
26
|
+
console.log('- Turn on coffee maker');
|
|
27
|
+
console.log('- Adjust thermostat');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
automator.addFunction('weeklyBackup', function(payload) {
|
|
31
|
+
console.log('Running weekly backup...');
|
|
32
|
+
// Simulate backup process
|
|
33
|
+
console.log('Backup complete!');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Listen to events
|
|
37
|
+
automator.on('ready', () => {
|
|
38
|
+
console.log('Automator started!');
|
|
39
|
+
console.log('Current actions:', automator.getActions().length);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
automator.on('action', (event) => {
|
|
43
|
+
console.log(`\n=== Action Executed ===`);
|
|
44
|
+
console.log(`Name: ${event.name || 'Unnamed'}`);
|
|
45
|
+
console.log(`Command: ${event.cmd}`);
|
|
46
|
+
console.log(`======================\n`);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
automator.on('error', (event) => {
|
|
50
|
+
console.error('Error:', event.message);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Add actions
|
|
54
|
+
|
|
55
|
+
// 1. Every 10 seconds - demo message
|
|
56
|
+
automator.addAction({
|
|
57
|
+
name: 'Demo Message',
|
|
58
|
+
cmd: 'logMessage',
|
|
59
|
+
date: new Date(Date.now() + 5000), // Start in 5 seconds
|
|
60
|
+
payload: { message: 'This is a recurring message every 10 seconds' },
|
|
61
|
+
unBuffered: false,
|
|
62
|
+
repeat: {
|
|
63
|
+
type: 'second',
|
|
64
|
+
interval: 10,
|
|
65
|
+
limit: 6, // Run 6 times then stop
|
|
66
|
+
dstPolicy: 'once'
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// 2. Daily morning routine at 7:00 AM
|
|
71
|
+
automator.addAction({
|
|
72
|
+
name: 'Morning Routine',
|
|
73
|
+
cmd: 'morningRoutine',
|
|
74
|
+
date: new Date(new Date().setHours(7, 0, 0, 0)),
|
|
75
|
+
unBuffered: false,
|
|
76
|
+
repeat: {
|
|
77
|
+
type: 'day',
|
|
78
|
+
interval: 1,
|
|
79
|
+
dstPolicy: 'once'
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// 3. Weekly backup every Sunday at 2:00 AM
|
|
84
|
+
const nextSunday = new Date();
|
|
85
|
+
nextSunday.setDate(nextSunday.getDate() + (7 - nextSunday.getDay()) % 7);
|
|
86
|
+
nextSunday.setHours(2, 0, 0, 0);
|
|
87
|
+
|
|
88
|
+
automator.addAction({
|
|
89
|
+
name: 'Weekly Backup',
|
|
90
|
+
cmd: 'weeklyBackup',
|
|
91
|
+
date: nextSunday,
|
|
92
|
+
unBuffered: false,
|
|
93
|
+
repeat: {
|
|
94
|
+
type: 'week',
|
|
95
|
+
interval: 1,
|
|
96
|
+
dstPolicy: 'once'
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Demonstrate simulation - what will happen in the next 24 hours?
|
|
101
|
+
console.log('\n=== Simulation: Next 24 hours ===');
|
|
102
|
+
const now = new Date();
|
|
103
|
+
const tomorrow = new Date(now.getTime() + 24 * 60 * 60 * 1000);
|
|
104
|
+
|
|
105
|
+
const futureEvents = automator.getActionsInRange(now, tomorrow);
|
|
106
|
+
console.log(`${futureEvents.length} events scheduled in the next 24 hours\n`);
|
|
107
|
+
|
|
108
|
+
futureEvents.slice(0, 10).forEach((event, i) => {
|
|
109
|
+
console.log(`${i + 1}. ${event.name} at ${event.scheduledTime.toLocaleString()}`);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
if (futureEvents.length > 10) {
|
|
113
|
+
console.log(`... and ${futureEvents.length - 10} more events`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
console.log('\n=== Current Actions ===');
|
|
117
|
+
automator.getActions().forEach((action) => {
|
|
118
|
+
console.log(automator.describeAction(action.id));
|
|
119
|
+
console.log('---');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Start the automator
|
|
123
|
+
console.log('\n=== Starting Automator ===\n');
|
|
124
|
+
automator.start();
|
|
125
|
+
|
|
126
|
+
// Graceful shutdown on Ctrl+C
|
|
127
|
+
process.on('SIGINT', () => {
|
|
128
|
+
console.log('\n\nShutting down automator...');
|
|
129
|
+
automator.stop();
|
|
130
|
+
console.log('Goodbye!');
|
|
131
|
+
process.exit(0);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Keep the process running
|
|
135
|
+
console.log('Press Ctrl+C to stop\n');
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hello World - jw-automator v3
|
|
3
|
+
*
|
|
4
|
+
* The simplest possible example.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const Automator = require('../index');
|
|
8
|
+
|
|
9
|
+
// Create automator (in-memory, no persistence)
|
|
10
|
+
const automator = new Automator({
|
|
11
|
+
storage: Automator.storage.memory()
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
// Register a function
|
|
15
|
+
automator.addFunction('sayHello', function() {
|
|
16
|
+
console.log('Hello, World!');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// Add an action that runs every 3 seconds, 5 times
|
|
20
|
+
automator.addAction({
|
|
21
|
+
cmd: 'sayHello',
|
|
22
|
+
date: new Date(Date.now() + 1000), // Start in 1 second
|
|
23
|
+
repeat: {
|
|
24
|
+
type: 'second',
|
|
25
|
+
interval: 3,
|
|
26
|
+
limit: 5
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Start the scheduler
|
|
31
|
+
automator.start();
|
|
32
|
+
|
|
33
|
+
console.log('Automator started! Will say hello 5 times, every 3 seconds.');
|
|
34
|
+
|
|
35
|
+
// Auto-stop after 20 seconds
|
|
36
|
+
setTimeout(() => {
|
|
37
|
+
automator.stop();
|
|
38
|
+
console.log('Goodbye!');
|
|
39
|
+
process.exit(0);
|
|
40
|
+
}, 20000);
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IoT Sensor Example - jw-automator v3
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates using jw-automator for sensor reading and data collection
|
|
5
|
+
* typical in IoT and home automation scenarios.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const Automator = require('../index');
|
|
9
|
+
|
|
10
|
+
// Create automator
|
|
11
|
+
const automator = new Automator({
|
|
12
|
+
storage: Automator.storage.memory() // Use memory storage for this example
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// Simulated sensor data
|
|
16
|
+
const sensorData = {
|
|
17
|
+
temperature: 22.5,
|
|
18
|
+
humidity: 45,
|
|
19
|
+
pressure: 1013.25
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// Simulate sensor reading
|
|
23
|
+
function readSensor() {
|
|
24
|
+
sensorData.temperature += (Math.random() - 0.5) * 2;
|
|
25
|
+
sensorData.humidity += (Math.random() - 0.5) * 5;
|
|
26
|
+
sensorData.pressure += (Math.random() - 0.5) * 2;
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
temperature: sensorData.temperature.toFixed(2),
|
|
30
|
+
humidity: sensorData.humidity.toFixed(1),
|
|
31
|
+
pressure: sensorData.pressure.toFixed(2),
|
|
32
|
+
timestamp: new Date()
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Register sensor functions
|
|
37
|
+
automator.addFunction('readTemperature', function() {
|
|
38
|
+
const reading = readSensor();
|
|
39
|
+
console.log(`[TEMP] ${reading.temperature}°C at ${reading.timestamp.toLocaleTimeString()}`);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
automator.addFunction('readHumidity', function() {
|
|
43
|
+
const reading = readSensor();
|
|
44
|
+
console.log(`[HUMID] ${reading.humidity}% at ${reading.timestamp.toLocaleTimeString()}`);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
automator.addFunction('fullSensorSweep', function() {
|
|
48
|
+
const reading = readSensor();
|
|
49
|
+
console.log('\n=== Full Sensor Sweep ===');
|
|
50
|
+
console.log(`Time: ${reading.timestamp.toLocaleString()}`);
|
|
51
|
+
console.log(`Temperature: ${reading.temperature}°C`);
|
|
52
|
+
console.log(`Humidity: ${reading.humidity}%`);
|
|
53
|
+
console.log(`Pressure: ${reading.pressure} hPa`);
|
|
54
|
+
console.log('========================\n');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
automator.addFunction('dailyReport', function() {
|
|
58
|
+
console.log('\n*** DAILY SENSOR REPORT ***');
|
|
59
|
+
console.log('Generated at:', new Date().toLocaleString());
|
|
60
|
+
console.log('Current readings:', readSensor());
|
|
61
|
+
console.log('Report complete.');
|
|
62
|
+
console.log('***************************\n');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Add sensor reading actions
|
|
66
|
+
|
|
67
|
+
// 1. Temperature reading every 5 seconds (for 1 minute demo)
|
|
68
|
+
automator.addAction({
|
|
69
|
+
name: 'Temperature Reading',
|
|
70
|
+
cmd: 'readTemperature',
|
|
71
|
+
date: new Date(Date.now() + 2000),
|
|
72
|
+
unBuffered: false, // Catch up if delayed
|
|
73
|
+
repeat: {
|
|
74
|
+
type: 'second',
|
|
75
|
+
interval: 5,
|
|
76
|
+
limit: 12 // Stop after 12 readings (1 minute)
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// 2. Humidity reading every 10 seconds
|
|
81
|
+
automator.addAction({
|
|
82
|
+
name: 'Humidity Reading',
|
|
83
|
+
cmd: 'readHumidity',
|
|
84
|
+
date: new Date(Date.now() + 3000),
|
|
85
|
+
unBuffered: false,
|
|
86
|
+
repeat: {
|
|
87
|
+
type: 'second',
|
|
88
|
+
interval: 10,
|
|
89
|
+
limit: 6 // Stop after 6 readings
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// 3. Full sensor sweep every 30 seconds
|
|
94
|
+
automator.addAction({
|
|
95
|
+
name: 'Full Sensor Sweep',
|
|
96
|
+
cmd: 'fullSensorSweep',
|
|
97
|
+
date: new Date(Date.now() + 5000),
|
|
98
|
+
unBuffered: false,
|
|
99
|
+
repeat: {
|
|
100
|
+
type: 'second',
|
|
101
|
+
interval: 30,
|
|
102
|
+
limit: 3 // Stop after 3 sweeps
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// 4. Daily report at 6:00 AM (won't run in this demo, but shows the pattern)
|
|
107
|
+
const tomorrow6AM = new Date();
|
|
108
|
+
tomorrow6AM.setDate(tomorrow6AM.getDate() + 1);
|
|
109
|
+
tomorrow6AM.setHours(6, 0, 0, 0);
|
|
110
|
+
|
|
111
|
+
automator.addAction({
|
|
112
|
+
name: 'Daily Report',
|
|
113
|
+
cmd: 'dailyReport',
|
|
114
|
+
date: tomorrow6AM,
|
|
115
|
+
unBuffered: false,
|
|
116
|
+
repeat: {
|
|
117
|
+
type: 'day',
|
|
118
|
+
interval: 1
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Events
|
|
123
|
+
automator.on('ready', () => {
|
|
124
|
+
console.log('IoT Sensor Monitor Started');
|
|
125
|
+
console.log('================================\n');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
automator.on('action', (event) => {
|
|
129
|
+
// Actions already log themselves
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Start
|
|
133
|
+
automator.start();
|
|
134
|
+
|
|
135
|
+
console.log('Sensor readings starting in 2 seconds...');
|
|
136
|
+
console.log('This demo will run for about 90 seconds.\n');
|
|
137
|
+
|
|
138
|
+
// Auto-stop after 90 seconds
|
|
139
|
+
setTimeout(() => {
|
|
140
|
+
console.log('\n\nDemo complete. Stopping automator...');
|
|
141
|
+
automator.stop();
|
|
142
|
+
|
|
143
|
+
console.log('\nFinal action summary:');
|
|
144
|
+
automator.getActions().forEach(action => {
|
|
145
|
+
console.log(`- ${action.name}: ${action.count} executions`);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
process.exit(0);
|
|
149
|
+
}, 90000);
|