0
# Time Series Analytics
1
2
Advanced time series data querying with batch operations, sampling, and historical data analysis. The SeriesV2Api provides powerful capabilities for retrieving and analyzing IoT sensor data over time.
3
4
## Capabilities
5
6
### Batch Query Operations
7
8
High-performance batch querying for multiple properties and time ranges simultaneously.
9
10
```javascript { .api }
11
class SeriesV2Api {
12
/**
13
* Execute batch query for multiple time series with aggregation
14
* @param batchQueryRequestsMediaV1 - Batch query configuration with multiple requests
15
* @param opts - Optional parameters including organization ID
16
* @returns Promise<ArduinoSeriesResponse> - Aggregated time series data
17
*/
18
seriesV2BatchQuery(batchQueryRequestsMediaV1: BatchQueryRequestsMediaV1, opts?: any): Promise<ArduinoSeriesResponse>;
19
20
/**
21
* Execute batch query for raw (non-aggregated) time series data
22
* @param batchQueryRawRequestsMediaV1 - Raw batch query configuration
23
* @param opts - Optional parameters including organization ID
24
* @returns Promise<ArduinoSeriesRawResponse> - Raw time series data
25
*/
26
seriesV2BatchQueryRaw(batchQueryRawRequestsMediaV1: BatchQueryRawRequestsMediaV1, opts?: any): Promise<ArduinoSeriesRawResponse>;
27
28
/**
29
* Get the last value for multiple properties in a single batch operation
30
* @param batchLastValueRequestsMediaV1 - Batch last value request configuration
31
* @param opts - Optional parameters including organization ID
32
* @returns Promise<ArduinoSeriesRawLastValueResponse> - Latest values for requested properties
33
*/
34
seriesV2BatchQueryRawLastValue(batchLastValueRequestsMediaV1: BatchLastValueRequestsMediaV1, opts?: any): Promise<ArduinoSeriesRawLastValueResponse>;
35
36
/**
37
* Execute batch query with statistical sampling for large datasets
38
* @param batchQuerySampledRequestsMediaV1 - Sampled batch query configuration
39
* @param opts - Optional parameters including organization ID
40
* @returns Promise<ArduinoSeriesSampledResponse> - Statistically sampled data
41
*/
42
seriesV2BatchQuerySampling(batchQuerySampledRequestsMediaV1: BatchQuerySampledRequestsMediaV1, opts?: any): Promise<ArduinoSeriesSampledResponse>;
43
44
/**
45
* Request historic data export for long-term analysis
46
* @param historicDataRequest - Historic data export request configuration
47
* @param opts - Optional parameters including organization ID
48
* @returns Promise<ArduinoSeriesResponse> - Historic data export information
49
*/
50
seriesV2HistoricData(historicDataRequest: HistoricDataRequest, opts?: any): Promise<ArduinoSeriesResponse>;
51
}
52
```
53
54
### Legacy Time Series Operations (V1 API)
55
56
Legacy time series functionality for backward compatibility. Use SeriesV2Api for new integrations.
57
58
```javascript { .api }
59
class SeriesV1Api {
60
/**
61
* Execute batch query for time-series data (legacy)
62
* @param batchQueryRequestsMediaV1 - Batch query requests
63
* @returns Promise<ArduinoSeriesBatch> - Batch of time-series data
64
*/
65
seriesV1BatchQuery(batchQueryRequestsMediaV1: BatchQueryRequestsMediaV1): Promise<ArduinoSeriesBatch>;
66
67
/**
68
* Execute batch query for raw time-series data (legacy)
69
* @param batchQueryRawRequestsMediaV1 - Raw batch query requests
70
* @returns Promise<ArduinoSeriesRawBatch> - Raw batch of time-series data
71
*/
72
seriesV1BatchQueryRaw(batchQueryRawRequestsMediaV1: BatchQueryRawRequestsMediaV1): Promise<ArduinoSeriesRawBatch>;
73
}
74
```
75
76
**Migration Note:** V1 API has limited functionality compared to V2. V2 provides enhanced querying capabilities, better performance, statistical sampling, and improved data structures.
77
78
## Data Models
79
80
```javascript { .api }
81
interface BatchQueryRequestsMediaV1 {
82
requests?: BatchQueryRequestMediaV1[];
83
}
84
85
interface BatchQueryRequestMediaV1 {
86
aggregation?: string;
87
from?: Date;
88
q?: string;
89
series_limit?: number;
90
to?: Date;
91
}
92
93
interface BatchQueryRawRequestsMediaV1 {
94
requests?: BatchQueryRawRequestMediaV1[];
95
}
96
97
interface BatchQueryRawRequestMediaV1 {
98
from?: Date;
99
q?: string;
100
series_limit?: number;
101
to?: Date;
102
}
103
104
interface BatchLastValueRequestsMediaV1 {
105
requests?: BatchQueryRawLastValueRequestMediaV1[];
106
}
107
108
interface BatchQueryRawLastValueRequestMediaV1 {
109
q?: string;
110
}
111
112
interface BatchQuerySampledRequestsMediaV1 {
113
requests?: BatchQuerySampledRequestMediaV1[];
114
}
115
116
interface BatchQuerySampledRequestMediaV1 {
117
aggregation?: string;
118
from?: Date;
119
interval?: number;
120
q?: string;
121
series_limit?: number;
122
to?: Date;
123
}
124
125
interface HistoricDataRequest {
126
from?: Date;
127
properties?: string[];
128
thingIds?: string[];
129
to?: Date;
130
}
131
132
interface ArduinoSeriesResponse {
133
aggregation?: string;
134
countValues?: number;
135
data?: TimeseriesDataPoint[];
136
fromDate?: Date;
137
interval?: number;
138
query?: string;
139
serieLimit?: number;
140
status?: string;
141
thingId?: string;
142
toDate?: Date;
143
values?: any[];
144
}
145
146
interface ArduinoSeriesRawResponse {
147
data?: TimeseriesDataPoint[];
148
query?: string;
149
status?: string;
150
}
151
152
interface ArduinoSeriesSampledResponse {
153
aggregation?: string;
154
data?: TimeseriesDataPoint[];
155
interval?: number;
156
query?: string;
157
status?: string;
158
}
159
160
interface ArduinoSeriesRawLastValueResponse {
161
data?: ArduinoSeriesRawBatchLastvalue[];
162
query?: string;
163
status?: string;
164
}
165
166
interface TimeseriesDataPoint {
167
timestamp?: Date;
168
value?: any;
169
}
170
171
interface ArduinoSeriesRawBatchLastvalue {
172
lastValue?: any;
173
lastValueUpdatedAt?: Date;
174
propertyId?: string;
175
thingId?: string;
176
}
177
```
178
179
**Comprehensive Time Series Analytics Examples:**
180
181
```javascript
182
import ArduinoIotClient from '@arduino/arduino-iot-client';
183
184
const seriesApi = new ArduinoIotClient.SeriesV2Api();
185
186
// Multi-property batch analysis
187
async function analyzeSensorData(thingIds, propertyIds, timeRange) {
188
try {
189
const { from, to } = timeRange;
190
191
// Create batch query for multiple properties with different aggregations
192
const batchRequest = {
193
requests: [
194
// Temperature data with hourly averages
195
{
196
q: `property.id in ["${propertyIds.temperature}"] AND thing.id in ["${thingIds[0]}"]`,
197
from: from,
198
to: to,
199
aggregation: 'AVG',
200
series_limit: 1000
201
},
202
// Humidity data with 30-minute averages
203
{
204
q: `property.id in ["${propertyIds.humidity}"] AND thing.id in ["${thingIds[0]}"]`,
205
from: from,
206
to: to,
207
aggregation: 'AVG',
208
series_limit: 1000
209
},
210
// Pressure data with daily min/max
211
{
212
q: `property.id in ["${propertyIds.pressure}"] AND thing.id in ["${thingIds[0]}"]`,
213
from: from,
214
to: to,
215
aggregation: 'MAX',
216
series_limit: 100
217
}
218
]
219
};
220
221
const results = await seriesApi.seriesV2BatchQuery(batchRequest);
222
console.log(`Batch query returned ${results.data?.length || 0} data points`);
223
224
return results;
225
226
} catch (error) {
227
console.error('Batch query failed:', error);
228
throw error;
229
}
230
}
231
232
// Real-time dashboard data
233
async function getDashboardData(propertyIds) {
234
try {
235
// Get last values for all dashboard properties
236
const lastValueRequest = {
237
requests: propertyIds.map(pid => ({
238
q: `property.id = "${pid}"`
239
}))
240
};
241
242
const lastValues = await seriesApi.seriesV2BatchQueryRawLastValue(lastValueRequest);
243
244
console.log('Latest property values:');
245
lastValues.data?.forEach((item, index) => {
246
console.log(`Property ${propertyIds[index]}: ${item.lastValue}`);
247
});
248
249
// Get recent trend data for charts (last 4 hours)
250
const trendFrom = new Date(Date.now() - 4 * 3600000);
251
const trendTo = new Date();
252
253
const trendRequest = {
254
requests: propertyIds.map(pid => ({
255
q: `property.id = "${pid}"`,
256
from: trendFrom,
257
to: trendTo,
258
aggregation: 'AVG'
259
}))
260
};
261
262
const trendData = await seriesApi.seriesV2BatchQuery(trendRequest);
263
264
return {
265
lastValues: lastValues.data,
266
trendData: trendData.data
267
};
268
269
} catch (error) {
270
console.error('Dashboard data query failed:', error);
271
throw error;
272
}
273
}
274
275
// Statistical analysis with sampling
276
async function performStatisticalAnalysis(thingId, propertyId, days = 30) {
277
try {
278
const from = new Date(Date.now() - days * 24 * 3600000);
279
const to = new Date();
280
281
// Get sampled data for statistical analysis
282
const sampledRequest = {
283
requests: [{
284
q: `property.id = "${propertyId}" AND thing.id = "${thingId}"`,
285
from: from,
286
to: to,
287
aggregation: 'AVG',
288
interval: 3600, // 1-hour intervals
289
series_limit: days * 24 // Max one point per hour
290
}]
291
};
292
293
const sampledData = await seriesApi.seriesV2BatchQuerySampling(sampledRequest);
294
295
if (sampledData.data && sampledData.data.length > 0) {
296
const values = sampledData.data.map(point => parseFloat(point.value));
297
298
// Calculate statistics
299
const sortedValues = [...values].sort((a, b) => a - b);
300
const stats = {
301
count: values.length,
302
min: sortedValues[0],
303
max: sortedValues[sortedValues.length - 1],
304
mean: values.reduce((sum, val) => sum + val, 0) / values.length,
305
median: sortedValues[Math.floor(sortedValues.length / 2)],
306
q25: sortedValues[Math.floor(sortedValues.length * 0.25)],
307
q75: sortedValues[Math.floor(sortedValues.length * 0.75)],
308
stdDev: 0
309
};
310
311
// Calculate standard deviation
312
const variance = values.reduce((sum, val) => sum + Math.pow(val - stats.mean, 2), 0) / values.length;
313
stats.stdDev = Math.sqrt(variance);
314
315
console.log('Statistical Analysis Results:');
316
console.log(`Data points: ${stats.count}`);
317
console.log(`Range: ${stats.min.toFixed(2)} - ${stats.max.toFixed(2)}`);
318
console.log(`Mean: ${stats.mean.toFixed(2)} ± ${stats.stdDev.toFixed(2)}`);
319
console.log(`Median: ${stats.median.toFixed(2)}`);
320
console.log(`IQR: ${stats.q25.toFixed(2)} - ${stats.q75.toFixed(2)}`);
321
322
return stats;
323
}
324
325
return null;
326
327
} catch (error) {
328
console.error('Statistical analysis failed:', error);
329
throw error;
330
}
331
}
332
333
// Anomaly detection
334
async function detectAnomalies(thingId, propertyId, hours = 24) {
335
try {
336
const from = new Date(Date.now() - hours * 3600000);
337
const to = new Date();
338
339
// Get raw data for anomaly detection
340
const rawRequest = {
341
requests: [{
342
q: `property.id = "${propertyId}" AND thing.id = "${thingId}"`,
343
from: from,
344
to: to,
345
series_limit: 1000
346
}]
347
};
348
349
const rawData = await seriesApi.seriesV2BatchQueryRaw(rawRequest);
350
351
if (rawData.data && rawData.data.length > 0) {
352
const values = rawData.data.map(point => parseFloat(point.value));
353
const timestamps = rawData.data.map(point => new Date(point.timestamp));
354
355
// Calculate moving average and standard deviation
356
const windowSize = Math.min(10, Math.floor(values.length / 4));
357
const anomalies = [];
358
359
for (let i = windowSize; i < values.length; i++) {
360
const window = values.slice(i - windowSize, i);
361
const mean = window.reduce((sum, val) => sum + val, 0) / window.length;
362
const stdDev = Math.sqrt(
363
window.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / window.length
364
);
365
366
const currentValue = values[i];
367
const zScore = Math.abs((currentValue - mean) / stdDev);
368
369
// Detect outliers (z-score > 2)
370
if (zScore > 2) {
371
anomalies.push({
372
timestamp: timestamps[i],
373
value: currentValue,
374
zScore: zScore.toFixed(2),
375
expected: mean.toFixed(2)
376
});
377
}
378
}
379
380
console.log(`Detected ${anomalies.length} anomalies in the last ${hours} hours`);
381
anomalies.forEach(anomaly => {
382
console.log(`${anomaly.timestamp.toISOString()}: ${anomaly.value} (z-score: ${anomaly.zScore})`);
383
});
384
385
return anomalies;
386
}
387
388
return [];
389
390
} catch (error) {
391
console.error('Anomaly detection failed:', error);
392
throw error;
393
}
394
}
395
396
// Historic data export
397
async function exportHistoricData(thingIds, propertyIds, exportPeriod) {
398
try {
399
const { from, to } = exportPeriod;
400
401
const exportRequest = {
402
thingIds: thingIds,
403
properties: propertyIds,
404
from: from,
405
to: to
406
};
407
408
const exportInfo = await seriesApi.seriesV2HistoricData(exportRequest);
409
410
console.log('Historic data export initiated:');
411
console.log(`Status: ${exportInfo.status}`);
412
console.log(`Time range: ${from.toISOString()} to ${to.toISOString()}`);
413
console.log(`Properties: ${propertyIds.length}`);
414
console.log(`Things: ${thingIds.length}`);
415
416
return exportInfo;
417
418
} catch (error) {
419
console.error('Historic data export failed:', error);
420
throw error;
421
}
422
}
423
424
// Comprehensive analytics dashboard
425
async function createAnalyticsDashboard(thingIds, propertyIds) {
426
try {
427
const now = new Date();
428
const lastHour = new Date(now - 3600000);
429
const lastDay = new Date(now - 24 * 3600000);
430
const lastWeek = new Date(now - 7 * 24 * 3600000);
431
432
console.log('Creating comprehensive analytics dashboard...');
433
434
// 1. Real-time status
435
console.log('\n=== Real-time Status ===');
436
const realtimeData = await getDashboardData(propertyIds);
437
438
// 2. Last hour trends
439
console.log('\n=== Last Hour Analysis ===');
440
const hourlyTrends = await analyzeSensorData(thingIds, {
441
temperature: propertyIds[0],
442
humidity: propertyIds[1],
443
pressure: propertyIds[2]
444
}, { from: lastHour, to: now });
445
446
// 3. Daily statistics
447
console.log('\n=== Daily Statistics ===');
448
for (const propertyId of propertyIds) {
449
await performStatisticalAnalysis(thingIds[0], propertyId, 1);
450
}
451
452
// 4. Anomaly detection
453
console.log('\n=== Anomaly Detection ===');
454
for (const propertyId of propertyIds) {
455
await detectAnomalies(thingIds[0], propertyId, 24);
456
}
457
458
// 5. Weekly export
459
console.log('\n=== Weekly Data Export ===');
460
await exportHistoricData(thingIds, propertyIds, {
461
from: lastWeek,
462
to: now
463
});
464
465
console.log('\nAnalytics dashboard completed successfully');
466
467
} catch (error) {
468
if (error.status === 400) {
469
console.error('Bad request - check query parameters');
470
} else if (error.status === 429) {
471
console.error('Rate limit exceeded - slow down queries');
472
} else {
473
console.error('Analytics error:', error.detail || error.message);
474
}
475
}
476
}
477
478
// Usage example
479
async function timeSeriesAnalyticsDemo() {
480
const thingIds = ['your-thing-id-1', 'your-thing-id-2'];
481
const propertyIds = ['temp-prop-id', 'humidity-prop-id', 'pressure-prop-id'];
482
483
await createAnalyticsDashboard(thingIds, propertyIds);
484
}
485
```