/**
* RSSI Sparkline Component
* SVG-based real-time RSSI visualization
*/
const RSSISparkline = (function() {
'use strict';
// Default configuration
const DEFAULT_CONFIG = {
width: 80,
height: 24,
maxSamples: 30,
strokeWidth: 1.5,
minRssi: -100,
maxRssi: -30,
showCurrentValue: true,
showGradient: true,
animateUpdates: true
};
// Color thresholds based on RSSI
const RSSI_COLORS = {
excellent: { rssi: -50, color: '#22c55e' }, // Green
good: { rssi: -60, color: '#84cc16' }, // Lime
fair: { rssi: -70, color: '#eab308' }, // Yellow
weak: { rssi: -80, color: '#f97316' }, // Orange
poor: { rssi: -100, color: '#ef4444' } // Red
};
/**
* Get color for RSSI value
*/
function getRssiColor(rssi) {
if (rssi >= RSSI_COLORS.excellent.rssi) return RSSI_COLORS.excellent.color;
if (rssi >= RSSI_COLORS.good.rssi) return RSSI_COLORS.good.color;
if (rssi >= RSSI_COLORS.fair.rssi) return RSSI_COLORS.fair.color;
if (rssi >= RSSI_COLORS.weak.rssi) return RSSI_COLORS.weak.color;
return RSSI_COLORS.poor.color;
}
/**
* Normalize RSSI value to 0-1 range
*/
function normalizeRssi(rssi, min, max) {
return Math.max(0, Math.min(1, (rssi - min) / (max - min)));
}
/**
* Create sparkline SVG element
*/
function createSparklineSvg(samples, config = {}) {
const cfg = { ...DEFAULT_CONFIG, ...config };
const { width, height, minRssi, maxRssi, strokeWidth, showGradient } = cfg;
if (!samples || samples.length < 2) {
return createEmptySparkline(width, height);
}
// Normalize samples
const normalized = samples.map(s => {
const rssi = typeof s === 'object' ? s.rssi : s;
return {
value: normalizeRssi(rssi, minRssi, maxRssi),
rssi: rssi
};
});
// Calculate path
const stepX = width / (normalized.length - 1);
let pathD = '';
let areaD = '';
const points = [];
normalized.forEach((sample, i) => {
const x = i * stepX;
const y = height - (sample.value * (height - 2)) - 1; // 1px padding top/bottom
points.push({ x, y, rssi: sample.rssi });
if (i === 0) {
pathD = `M${x.toFixed(1)},${y.toFixed(1)}`;
areaD = `M${x.toFixed(1)},${height} L${x.toFixed(1)},${y.toFixed(1)}`;
} else {
pathD += ` L${x.toFixed(1)},${y.toFixed(1)}`;
areaD += ` L${x.toFixed(1)},${y.toFixed(1)}`;
}
});
// Close area path
areaD += ` L${width},${height} Z`;
// Get current color based on latest value
const latestRssi = normalized[normalized.length - 1].rssi;
const strokeColor = getRssiColor(latestRssi);
// Create SVG
const gradientId = `sparkline-gradient-${Math.random().toString(36).substr(2, 9)}`;
let gradientDef = '';
if (showGradient) {
gradientDef = `