barnacles-influxdb2
Version:
Write ambient IoT sensor timeseries data to InfluxDB v2. We believe in an open Internet of Things.
203 lines (175 loc) • 5.84 kB
JavaScript
/**
* Copyright reelyActive 2023-2025
* We believe in an open Internet of Things
*/
const { InfluxDB, Point } = require('@influxdata/influxdb-client');
const DEFAULT_INFLUX_URL = 'http://localhost:8086';
const DEFAULT_INFLUX_ORG = 'reelyActive';
const DEFAULT_INFLUX_BUCKET = 'pareto-anywhere';
const DEFAULT_PRINT_ERRORS = false;
const DEFAULT_RADDEC_OPTIONS = { includePackets: false };
const DEFAULT_DYNAMB_OPTIONS = {};
const DEFAULT_EVENTS_TO_STORE = { raddec: DEFAULT_RADDEC_OPTIONS,
dynamb: DEFAULT_DYNAMB_OPTIONS };
const SUPPORTED_EVENTS = [ 'raddec', 'dynamb' ];
const RADDEC_MEASUREMENT = 'raddec';
const DYNAMB_MEASUREMENT = 'dynamb';
/**
* BarnaclesInfluxDB2 Class
* Detects events and writes to InfluxDB2.
*/
class BarnaclesInfluxDB2 {
/**
* BarnaclesInfluxDB2 constructor
* @param {Object} options The options as a JSON object.
* @constructor
*/
constructor(options) {
let self = this;
options = options || {};
this.printErrors = options.printErrors || DEFAULT_PRINT_ERRORS;
this.eventsToStore = {};
let eventsToStore = options.eventsToStore || DEFAULT_EVENTS_TO_STORE;
for(const event in eventsToStore) {
let isSupportedEvent = SUPPORTED_EVENTS.includes(event);
if(isSupportedEvent) {
self.eventsToStore[event] = eventsToStore[event] ||
DEFAULT_EVENTS_TO_STORE[event];
}
}
// The (provided) InfluxDB2 client has already been instantiated
if(options.client) {
this.client = options.client;
}
// Create client using the provided or default url and token
else {
let url = options.url || DEFAULT_INFLUX_URL;
let token = options.token || process.env.INFLUXDB_TOKEN;
let org = options.org || DEFAULT_INFLUX_ORG;
let bucket = options.bucket || DEFAULT_INFLUX_BUCKET;
this.influxDB = new InfluxDB({ url, token });
this.writeClient = this.influxDB.getWriteApi(org, bucket, 'ns');
}
}
/**
* Handle an outbound event.
* @param {String} name The outbound event name.
* @param {Object} data The outbound event data.
*/
handleEvent(name, data) {
let self = this;
let isEventToStore = self.eventsToStore.hasOwnProperty(name);
if(isEventToStore) {
switch(name) {
case 'raddec':
return handleRaddec(self, data);
case 'dynamb':
return handleDynamb(self, data);
}
}
}
}
/**
* Handle the given raddec by storing it in InfluxDB2.
* @param {BarnaclesInfluxDB2} instance The BarnaclesInfluxDB2 instance.
* @param {Object} raddec The raddec data.
*/
function handleRaddec(instance, raddec) {
let raddecOptions = instance.eventsToStore['raddec'];
// TODO: store raddecs in InfluxDB???
}
/**
* Handle the given dynamb by storing it in InfluxDB2.
* @param {BarnaclesInfluxDB2} instance The BarnaclesInfluxDB2 instance.
* @param {Object} dynamb The dynamb data.
*/
function handleDynamb(instance, dynamb) {
let dynambOptions = instance.eventsToStore['dynamb'];
let point = new Point(DYNAMB_MEASUREMENT)
.tag('deviceId', dynamb.deviceId)
.tag('deviceIdType', dynamb.deviceIdType)
.timestamp(new Date(dynamb.timestamp));
for(const property in dynamb) {
let value = dynamb[property];
switch(property) {
// Floats
case 'ammoniaConcentration':
case 'amperage':
case 'angleOfRotation':
case 'angularVelocity':
case 'batteryPercentage':
case 'batteryVoltage':
case 'carbonDioxideConcentration':
case 'carbonMonoxideConcentration':
case 'distance':
case 'elevation':
case 'heading':
case 'heartRate':
case 'illuminance':
case 'levelPercentage':
case 'luminousFlux':
case 'methaneConcentration':
case 'nitrogenDioxideConcentration':
case 'pressure':
case 'relativeHumidity':
case 'soundPressure':
case 'speed':
case 'temperature':
case 'volatileOrganicCompoundsConcentration':
case 'voltage':
point.floatField(property, value);
break;
// Booleans
case 'isHealthy':
point.booleanField(property, value);
// Booleans based on an array
case 'isButtonPressed':
case 'isContactDetected':
case 'isLiquidDetected':
case 'isMotionDetected':
if(Array.isArray(value)) {
let isAnyTrue = value.reduce((a, b) => a || b, false);
point.booleanField(property, isAnyTrue);
}
break;
// Unsigned integers
case 'numberOfOccupants':
case 'numberOfReceivedDevices':
case 'numberOfStrongestReceivedDevices':
case 'txCount':
case 'uptime':
point.uintField(property, value);
break;
// Unsigned integers based on sum of an array
case 'passageCounts':
if(Array.isArray(value)) {
let sum = value.reduce((a, b) => a + b, 0);
point.uintField(property, sum);
}
break;
// Floats based on RMS of an array
case 'acceleration':
case 'amperages':
case 'magneticField':
case 'pressures':
case 'temperatures':
case 'voltages':
if(Array.isArray(value)) {
let sumOfSquares = 0;
let numberOfElements = 0;
value.forEach(element => {
if(Number.isFinite(element)) {
sumOfSquares += (element * element);
numberOfElements++;
}
});
if(numberOfElements === 0) break;
let rms = Math.sqrt(sumOfSquares / numberOfElements);
point.floatField(property, rms);
}
break;
}
}
instance.writeClient.writePoint(point);
}
module.exports = BarnaclesInfluxDB2;