or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

automation-triggers.mddashboard-management.mddevice-management.mdindex.mdlora-device-management.mdnetwork-credentials.mdproperty-management.mdthing-management.mdtimeseries-analytics.md

timeseries-analytics.mddocs/

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

```