diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Diagnostics/Api/SystemController.php b/src/opnsense/mvc/app/controllers/OPNsense/Diagnostics/Api/SystemController.php index b4f585c01..048b92293 100644 --- a/src/opnsense/mvc/app/controllers/OPNsense/Diagnostics/Api/SystemController.php +++ b/src/opnsense/mvc/app/controllers/OPNsense/Diagnostics/Api/SystemController.php @@ -236,10 +236,12 @@ class SystemController extends ApiControllerBase foreach ($temps as $name => $value) { $tempItem = []; $tempItem['device'] = $name; - $tempItem['device_seq'] = filter_var($tempItem['device'], FILTER_SANITIZE_NUMBER_INT); /* XXX too opportunistic */ + $tempItem['device_seq'] = (int)filter_var($tempItem['device'], FILTER_SANITIZE_NUMBER_INT); $tempItem['temperature'] = trim(str_replace('C', '', $value)); - $tempItem['type_translated'] = gettext('CPU'); - $tempItem['type'] = 'cpu'; + $tempItem['type_translated'] = gettext('Other'); + $tempItem['type'] = 'other'; + + /* try to categorize a few of the readings just for labels */ if (str_starts_with($tempItem['device'], 'hw.acpi.')) { $tempItem['type_translated'] = gettext('Zone'); $tempItem['type'] = 'zone'; @@ -247,9 +249,13 @@ class SystemController extends ApiControllerBase $tempItem['type_translated'] = gettext('AMD'); $tempItem['type'] = 'amd'; } else if (str_starts_with($tempItem['device'], 'dev.pchtherm.')) { - $tempItem['type'] = 'platform'; $tempItem['type_translated'] = gettext('Platform'); + $tempItem['type'] = 'platform'; + } else if (str_starts_with($tempItem['device'], 'dev.cpu.')) { + $tempItem['type_translated'] = gettext('CPU'); + $tempItem['type'] = 'cpu'; } + $result[] = $tempItem; } diff --git a/src/opnsense/www/js/widgets/ThermalSensors.js b/src/opnsense/www/js/widgets/ThermalSensors.js index c7071f894..cc8792045 100644 --- a/src/opnsense/www/js/widgets/ThermalSensors.js +++ b/src/opnsense/www/js/widgets/ThermalSensors.js @@ -25,19 +25,22 @@ */ export default class ThermalSensors extends BaseWidget { - constructor() { - super(); + constructor(config) { + super(config); this.chart = null; this.width = null; this.height = null; this.colors = []; + + this.configurable = true; + this.cachedSensors = []; // prevent fetch when loading options } getMarkup() { return $(`
-
+
@@ -70,20 +73,20 @@ export default class ThermalSensors extends BaseWidget { } return this.colors; }, + categoryPercentage: 1.0, barPercentage: 0.8, borderWidth: 1, borderSkipped: false, borderRadius: 20, - barThickness: 20 }, { + categoryPercentage: 1.0, data: [], backgroundColor: ['#E5E5E5'], borderRadius: 20, barPercentage: 0.5, borderWidth: 1, borderSkipped: true, - barThickness: 10, pointHitRadius: 0 } ] @@ -98,15 +101,20 @@ export default class ThermalSensors extends BaseWidget { } let count = data.datasets[0].data.length; ctx.save(); + + const margin = 10; + ctx.textBaseline = 'middle'; for (let i = 0; i < count; i++) { const meta = chart.getDatasetMeta(0); const xPos = meta.data[i].x; const yPos = meta.data[i].y; const barHeight = meta.data[i].height; + const textX = Math.max(chartArea.left + margin, xPos - 50); + ctx.font = 'semibold 12px sans-serif'; ctx.fillStyle = '#ffffff'; - ctx.fillText(`${data.datasets[0].data[i]}°C`, xPos - 50, yPos + barHeight / 4); + ctx.fillText(`${data.datasets[0].data[i]}°C`, textX, yPos); } ctx.restore(); } @@ -177,33 +185,74 @@ export default class ThermalSensors extends BaseWidget { $('.thermalsensors-info-icon').tooltip({container: 'body'}); } + async onWidgetOptionsChanged(options) { + // Intentionally not awaited to avoid blocking dialog close + this._updateSensors(); + } + + async getWidgetOptions() { + const data = this.cachedSensors.length > 0 ? this.cachedSensors : await this._fetchSensors(); + + return { + sensors: { + title: this.translations.title, + type: 'select_multiple', + options: data.map(({ device, device_seq, type_translated }) => { + return { + value: device, + label: `${type_translated} ${device_seq}`, + }; + }), + default:data.map(({ device }) => device), + }, + }; + } + async onWidgetTick() { + const data = await this._fetchSensors(); + this.cachedSensors = this._parseSensors(data); + this._updateSensors(); + } + + async _fetchSensors() { const data = await this.ajaxCall('/api/diagnostics/system/systemTemperature'); - if (!data || !data.length) { + return data; + } + + async _updateSensors() { + const data = this.cachedSensors; + + if (!this.chart || data.length === 0) { $(`.${this.id}-chart-container`).html(` ${this.translations.unconfigured} `).css('margin', '2em auto') return; } - let parsed = this._parseSensors(data); - this._update(parsed); - } - _update(data = []) { - if (!this.chart || data.length === 0) { - return; - } + const config = await this.getWidgetConfig(); this.colors = new Array(data.length).fill(0); - data.forEach((value, index) => { - this.chart.data.labels[index] = `${value.type_translated} ${value.device_seq}`; - this.chart.data.datasets[0].data[index] = Math.max(1, Math.min(100, value.temperature)); - this.chart.data.datasets[0].metadata[index] = value; - this.chart.data.datasets[1].data[index] = 100 - value.temperature; + this.chart.data.labels = []; + this.chart.data.datasets[0].data = []; + this.chart.data.datasets[0].metadata = []; + this.chart.data.datasets[1].data = []; + + data.forEach((value) => { + if (!config.sensors.includes(value.device)) { + return; + } + this.chart.data.labels.push(`${value.type_translated} ${value.device_seq}`); + this.chart.data.datasets[0].data.push(Math.max(1, Math.min(100, value.temperature))); + this.chart.data.datasets[0].metadata.push(value); + this.chart.data.datasets[1].data.push(100 - value.temperature); }); this.chart.canvas.parentNode.style.height = `${30 + (data.length * 30)}px`; this.chart.update(); + + // Since we are modifying the chart height based on the data length, + // make sure we force the manager to recalculate the widget size. + this.config.callbacks.updateGrid(); } _parseSensors(data) {