diff --git a/README.md b/README.md index 072c939..3c052ce 100644 --- a/README.md +++ b/README.md @@ -109,14 +109,16 @@ Sensor data is obtained from the system using hwmon and GTop. Core Coding and th | Description | Command | | --- | --- | -| Launch preferences | `gnome-shell-extension-prefs Vitals@CoreCoding.com` | +| Launch preferences | `gnome-extensions prefs Vitals@CoreCoding.com` | | View logs | ``journalctl --since="`date '+%Y-%m-%d %H:%M'`" -f \| grep Vitals`` | | Compile schemas | `glib-compile-schemas --strict schemas/` | | Compile translation file | `msgfmt vitals.po -o vitals.mo` | -| Launch Wayland virtual window | `dbus-run-session -- gnome-shell --nested --wayland` | +| Launch Wayland virtual window | `dbus-run-session -- gnome-shell --devkit --wayland` | | Read hot-sensors value | `dconf read /org/gnome/shell/extensions/vitals/hot-sensors` | | Write hot-sensors value | `dconf write /org/gnome/shell/extensions/vitals/hot-sensors "['_memory_usage_', '_system_load_1m_']"`
This value configures the list of sensors that show up in the panel. To specify a sensor name, click on the extension to show the drop-down menu, then take the category label and the label of the individual sensor, convert them to `snake_case`, and format them like this: `_category_sensor_`.| +GNOME Shell 50 is supported. For older GNOME Shell versions that do not support `--devkit`, use `dbus-run-session -- gnome-shell --nested --wayland` when testing in a nested session. + ## Donations [Please consider donating if you find this extension useful.](https://corecoding.com/donate.php) diff --git a/extension.js b/extension.js index 470b28d..98b1af4 100644 --- a/extension.js +++ b/extension.js @@ -106,6 +106,17 @@ var VitalsMenuButton = GObject.registerClass({ this._initializeTimer(); } + _getActor(item) { + return item?.actor ?? item; + } + + _closeMenu() { + if (this.menu?.close) + this.menu.close(); + else if (this.menu?._getTopMenu) + this.menu._getTopMenu().close(); + } + _initializeMenu() { // display sensor categories for (let sensor in this._sensorIcons) { @@ -157,7 +168,7 @@ var VitalsMenuButton = GObject.registerClass({ // custom round monitor button let monitorButton = this._createRoundButton('org.gnome.SystemMonitor-symbolic', _('System Monitor')); monitorButton.connect('clicked', (self) => { - this.menu._getTopMenu().close(); + this._closeMenu(); Util.spawn(this._settings.get_string('monitor-cmd').split(" ")); }); customButtonBox.add_child(monitorButton); @@ -165,13 +176,13 @@ var VitalsMenuButton = GObject.registerClass({ // custom round preferences button let prefsButton = this._createRoundButton('preferences-system-symbolic', _('Preferences')); prefsButton.connect('clicked', (self) => { - this.menu._getTopMenu().close(); + this._closeMenu(); this._extensionObject.openPreferences(); }); customButtonBox.add_child(prefsButton); // now add the buttons to the top bar - item.actor.add_child(customButtonBox); + this._getActor(item).add_child(customButtonBox); // add buttons this.menu.addMenuItem(item); @@ -397,14 +408,15 @@ var VitalsMenuButton = GObject.registerClass({ _initializeMenuGroup(groupName, optionName, menuSuffix = '', position = -1) { this._groups[groupName] = new PopupMenu.PopupSubMenuMenuItem(_(this._ucFirst(groupName) + menuSuffix), true); this._groups[groupName].icon.gicon = Gio.icon_new_for_string(this._sensorIconPath(groupName)); + const groupActor = this._getActor(this._groups[groupName]); // hide menu items that user has requested to not include if (!this._settings.get_boolean('show-' + optionName)) - this._groups[groupName].actor.hide(); + groupActor.hide(); if (!this._groups[groupName].status) { this._groups[groupName].status = this._defaultLabel(); - this._groups[groupName].actor.insert_child_at_index(this._groups[groupName].status, 4); + groupActor.insert_child_at_index(this._groups[groupName].status, 4); this._groups[groupName].status.text = _('No Data'); } @@ -504,13 +516,15 @@ var VitalsMenuButton = GObject.registerClass({ const sensorName = sensor.substr(5); if(sensorName === 'gpu') { for(let i = 1; i <= this._numGpus; i++) - this._groups[sensorName + '#' + i].visible = this._settings.get_boolean(sensor); + this._getActor(this._groups[sensorName + '#' + i]).visible = this._settings.get_boolean(sensor); } else - this._groups[sensorName].visible = this._settings.get_boolean(sensor); + this._getActor(this._groups[sensorName]).visible = this._settings.get_boolean(sensor); } _positionInPanelChanged() { - this.container.get_parent().remove_child(this.container); + const parent = this.container.get_parent(); + if (parent) + parent.remove_child(this.container); let position = this._positionInPanel(); // allows easily addressable boxes diff --git a/menuItem.js b/menuItem.js index cf112ab..e5d84b7 100644 --- a/menuItem.js +++ b/menuItem.js @@ -34,7 +34,11 @@ export const MenuItem = GObject.registerClass({ this._valueLabel.set_y_expand(true); this.add_child(this._valueLabel); - this.actor._delegate = this; + this._getActor()._delegate = this; + } + + _getActor() { + return this.actor ?? this; } get checked() { @@ -59,7 +63,7 @@ export const MenuItem = GObject.registerClass({ // prevents menu from being closed activate(event) { - this._checked = !this._checked; + this._checked = !this._checked; this._updateOrnament(); this.emit('toggle', event); } diff --git a/metadata.json b/metadata.json index 2fba3a9..d929d76 100644 --- a/metadata.json +++ b/metadata.json @@ -4,7 +4,7 @@ "name": "Vitals", "settings-schema": "org.gnome.shell.extensions.vitals", "shell-version": [ - "45", "46", "47", "48", "49" + "45", "46", "47", "48", "49", "50" ], "url": "https://github.com/corecoding/Vitals", "uuid": "Vitals@CoreCoding.com", diff --git a/sensors.js b/sensors.js index be8df5d..44b3cc0 100644 --- a/sensors.js +++ b/sensors.js @@ -81,6 +81,11 @@ export const Sensors = GObject.registerClass({ this._settingChangedSignals.push(this._settings.connect('changed::' + key, callback)); } + _parseNumber(value) { + const number = typeof value === 'number' ? value : Number.parseFloat(value); + return Number.isFinite(number) ? number : null; + } + _refreshIPAddress(callback) { // check IP address new FileModule.File('https://ipv4.corecoding.com').read().then(contents => { @@ -148,9 +153,9 @@ export const Sensors = GObject.registerClass({ if (values = lines.match(/MemFree:(\s+)(\d+) kB/)) memFree = values[2]; let used = total - avail - let utilized = used / total; + let utilized = total > 0 ? used / total : 0; let swapUsed = swapTotal - swapFree - let swapUtilized = swapUsed / swapTotal; + let swapUtilized = swapTotal > 0 ? swapUsed / swapTotal : 0; this._returnValue(callback, 'Usage', utilized, 'memory', 'percent'); this._returnValue(callback, 'memory', utilized, 'memory-group', 'percent'); @@ -197,13 +202,16 @@ export const Sensors = GObject.registerClass({ // make sure we have data to report if (this._last_processor['core'][cpu] > 0) { - let delta = (total - this._last_processor['core'][cpu]) / dwell; + let sampleDwell = dwell > 0 ? dwell : 1; + let delta = (total - this._last_processor['core'][cpu]) / sampleDwell; // /proc/stat provides overall usage for us under the 'cpu' heading if (cpu == 'cpu') { - delta = delta / cores; - this._returnValue(callback, 'processor', delta / 100, 'processor-group', 'percent'); - this._returnValue(callback, 'Usage', delta / 100, 'processor', 'percent'); + if (cores > 0) { + delta = delta / cores; + this._returnValue(callback, 'processor', delta / 100, 'processor-group', 'percent'); + this._returnValue(callback, 'Usage', delta / 100, 'processor', 'percent'); + } } else { this._returnValue(callback, _('Core %d').format(cpu.substr(3)), delta / 100, 'processor', 'percent'); } @@ -233,6 +241,9 @@ export const Sensors = GObject.registerClass({ if (value) freqs.push(parseFloat(value[2])); } + if (freqs.length === 0) + return; + let sum = freqs.reduce((a, b) => a + b); let hertz = (sum / freqs.length) * 1000 * 1000; this._returnValue(callback, 'Frequency', hertz, 'processor', 'hertz'); @@ -243,13 +254,17 @@ export const Sensors = GObject.registerClass({ this._returnValue(callback, 'Min frequency', min_hertz, 'processor', 'hertz'); }).catch(err => { }); // if frequency scaling is enabled, cpu-freq reports - } else if (Object.values(this._last_processor['speed']).length > 0) { - let sum = this._last_processor['speed'].reduce((a, b) => a + b); - let hertz = (sum / this._last_processor['speed'].length) * 1000; + } else { + const speeds = this._last_processor['speed'].filter(speed => Number.isFinite(speed)); + if (speeds.length === 0) + return; + + let sum = speeds.reduce((a, b) => a + b); + let hertz = (sum / speeds.length) * 1000; this._returnValue(callback, 'Frequency', hertz, 'processor', 'hertz'); - let max_hertz = this._last_processor['speed'].reduce((a, b) => Math.max(a, b)) * 1000; + let max_hertz = speeds.reduce((a, b) => Math.max(a, b)) * 1000; this._returnValue(callback, 'Max frequency', max_hertz, 'processor', 'hertz'); - let min_hertz = this._last_processor['speed'].reduce((a, b) => Math.min(a, b)) * 1000; + let min_hertz = speeds.reduce((a, b) => Math.min(a, b)) * 1000; this._returnValue(callback, 'Min frequency', min_hertz, 'processor', 'hertz'); } } @@ -325,11 +340,16 @@ export const Sensors = GObject.registerClass({ // if multiple wireless device, we use the last one for (let line of lines) { let netArray = line.trim().split(/\s+/); - let quality_pct = netArray[2].substr(0, netArray[2].length-1) / 70; - let signal = netArray[3].substr(0, netArray[3].length-1); + if (netArray.length < 4) + continue; - this._returnValue(callback, 'WiFi Link Quality', quality_pct, 'network', 'percent'); - this._returnValue(callback, 'WiFi Signal Level', signal, 'network', 'string'); + let quality = this._parseNumber(netArray[2].replace(/\.$/, '')); + let signal = this._parseNumber(netArray[3].replace(/\.$/, '')); + + if (quality !== null) + this._returnValue(callback, 'WiFi Link Quality', quality / 70, 'network', 'percent'); + if (signal !== null) + this._returnValue(callback, 'WiFi Signal Level', signal, 'network', 'string'); } }).catch(err => { }); } @@ -353,13 +373,22 @@ export const Sensors = GObject.registerClass({ new FileModule.File('/proc/diskstats').read("\n").then(lines => { for (let line of lines) { let loadArray = line.trim().split(/\s+/); + if (loadArray.length < 10) + continue; + if ('/dev/' + loadArray[2] == this._storageDevice) { - var read = (loadArray[5] * 512); - var write = (loadArray[9] * 512); + const readSectors = this._parseNumber(loadArray[5]); + const writeSectors = this._parseNumber(loadArray[9]); + if (readSectors === null || writeSectors === null) + break; + + const read = readSectors * 512; + const write = writeSectors * 512; + let sampleDwell = dwell > 0 ? dwell : 1; this._returnValue(callback, 'Read total', read, 'storage', 'storage'); this._returnValue(callback, 'Write total', write, 'storage', 'storage'); - this._returnValue(callback, 'Read rate', (read - this._lastRead) / dwell, 'storage', 'storage'); - this._returnValue(callback, 'Write rate', (write - this._lastWrite) / dwell, 'storage', 'storage'); + this._returnValue(callback, 'Read rate', (read - this._lastRead) / sampleDwell, 'storage', 'storage'); + this._returnValue(callback, 'Write rate', (write - this._lastWrite) / sampleDwell, 'storage', 'storage'); this._lastRead = read; this._lastWrite = write; break; @@ -414,9 +443,24 @@ export const Sensors = GObject.registerClass({ let output = {}; for (let line of lines) { let split = line.split('='); - output[split[0].replace('POWER_SUPPLY_', '')] = split[1]; + if (split.length < 2) + continue; + output[split[0].replace('POWER_SUPPLY_', '')] = split.slice(1).join('='); } + const voltageNow = this._parseNumber(output['VOLTAGE_NOW']); + const currentNow = this._parseNumber(output['CURRENT_NOW']); + const powerNowRaw = this._parseNumber(output['POWER_NOW']); + const capacity = this._parseNumber(output['CAPACITY']); + const chargeFull = this._parseNumber(output['CHARGE_FULL']); + const chargeFullDesign = this._parseNumber(output['CHARGE_FULL_DESIGN']); + const chargeNow = this._parseNumber(output['CHARGE_NOW']); + const voltageMinDesign = this._parseNumber(output['VOLTAGE_MIN_DESIGN']); + let energyFull = this._parseNumber(output['ENERGY_FULL']); + let energyFullDesign = this._parseNumber(output['ENERGY_FULL_DESIGN']); + let energyNow = this._parseNumber(output['ENERGY_NOW']); + let powerNow = powerNowRaw; + if ('STATUS' in output) { this._returnValue(callback, 'State', output['STATUS'], 'battery', ''); } @@ -425,71 +469,69 @@ export const Sensors = GObject.registerClass({ this._returnValue(callback, 'Cycles', output['CYCLE_COUNT'], 'battery', ''); } - if ('VOLTAGE_NOW' in output) { - this._returnValue(callback, 'Voltage', output['VOLTAGE_NOW'] / 1000, 'battery', 'in'); + if (voltageNow !== null) { + this._returnValue(callback, 'Voltage', voltageNow / 1000, 'battery', 'in'); } if ('CAPACITY_LEVEL' in output) { this._returnValue(callback, 'Level', output['CAPACITY_LEVEL'], 'battery', ''); } - if ('CAPACITY' in output) { - this._returnValue(callback, 'Percentage', output['CAPACITY'] / 100, 'battery', 'percent'); + if (capacity !== null) { + this._returnValue(callback, 'Percentage', capacity / 100, 'battery', 'percent'); } - if ('VOLTAGE_NOW' in output && 'CURRENT_NOW' in output && (!('POWER_NOW' in output))) { - output['POWER_NOW'] = (output['VOLTAGE_NOW'] * output['CURRENT_NOW']) / 1000000; + if (powerNow === null && voltageNow !== null && currentNow !== null) { + powerNow = (voltageNow * currentNow) / 1000000; } - if ('POWER_NOW' in output) { - const powerValue = ( - parseFloat(output['POWER_NOW']) * (output['STATUS'] === 'Discharging' ? -1 : 1) - ); + if (powerNow !== null) { + const powerValue = powerNow * (output['STATUS'] === 'Discharging' ? -1 : 1); this._returnValue(callback, 'Power Rate', powerValue, 'battery', 'watt'); this._returnValue(callback, 'battery', powerValue, 'battery-group', 'watt'); } - if ('CHARGE_FULL' in output && 'VOLTAGE_MIN_DESIGN' in output && (!('ENERGY_FULL' in output))) { - output['ENERGY_FULL'] = (output['CHARGE_FULL'] * output['VOLTAGE_MIN_DESIGN']) / 1000000; + if (energyFull === null && chargeFull !== null && voltageMinDesign !== null) { + energyFull = (chargeFull * voltageMinDesign) / 1000000; } - if ('ENERGY_FULL' in output) { - this._returnValue(callback, 'Energy (full)', output['ENERGY_FULL'], 'battery', 'watt-hour'); + if (energyFull !== null) { + this._returnValue(callback, 'Energy (full)', energyFull, 'battery', 'watt-hour'); } - if ('CHARGE_FULL_DESIGN' in output && 'VOLTAGE_MIN_DESIGN' in output && (!('ENERGY_FULL_DESIGN' in output))) { - output['ENERGY_FULL_DESIGN'] = (output['CHARGE_FULL_DESIGN'] * output['VOLTAGE_MIN_DESIGN']) / 1000000; + if (energyFullDesign === null && chargeFullDesign !== null && voltageMinDesign !== null) { + energyFullDesign = (chargeFullDesign * voltageMinDesign) / 1000000; } - if ('ENERGY_FULL_DESIGN' in output) { - this._returnValue(callback, 'Energy (design)', output['ENERGY_FULL_DESIGN'], 'battery', 'watt-hour'); + if (energyFullDesign !== null) { + this._returnValue(callback, 'Energy (design)', energyFullDesign, 'battery', 'watt-hour'); - if ('ENERGY_FULL' in output) { - this._returnValue(callback, 'Capacity', (output['ENERGY_FULL'] / output['ENERGY_FULL_DESIGN']), 'battery', 'percent'); + if (energyFull !== null && energyFullDesign > 0) { + this._returnValue(callback, 'Capacity', (energyFull / energyFullDesign), 'battery', 'percent'); } } - if ('VOLTAGE_MIN_DESIGN' in output && 'CHARGE_NOW' in output && (!('ENERGY_NOW' in output))) { - output['ENERGY_NOW'] = (output['VOLTAGE_MIN_DESIGN'] * output['CHARGE_NOW']) / 1000000; + if (energyNow === null && voltageMinDesign !== null && chargeNow !== null) { + energyNow = (voltageMinDesign * chargeNow) / 1000000; } - if ('ENERGY_NOW' in output) { - this._returnValue(callback, 'Energy (now)', output['ENERGY_NOW'], 'battery', 'watt-hour'); + if (energyNow !== null) { + this._returnValue(callback, 'Energy (now)', energyNow, 'battery', 'watt-hour'); } - if ('ENERGY_FULL' in output && 'ENERGY_NOW' in output && 'POWER_NOW' in output && output['POWER_NOW'] !== 0 && 'STATUS' in output && (output['STATUS'] == 'Charging' || output['STATUS'] == 'Discharging')) { + if (energyFull !== null && energyNow !== null && powerNow !== null && powerNow !== 0 && 'STATUS' in output && (output['STATUS'] == 'Charging' || output['STATUS'] == 'Discharging')) { let timeLeft = 0; // two different formulas depending on if we are charging or discharging if (output['STATUS'] == 'Charging') { - timeLeft = ((output['ENERGY_FULL'] - output['ENERGY_NOW']) / output['POWER_NOW']); + timeLeft = ((energyFull - energyNow) / powerNow); } else { - timeLeft = (output['ENERGY_NOW'] / Math.abs(output['POWER_NOW'])); + timeLeft = (energyNow / Math.abs(powerNow)); } // don't process Infinity values - if (timeLeft !== Infinity) { + if (Number.isFinite(timeLeft) && timeLeft >= 0) { if (this._battery_charge_status != output['STATUS']) { // clears history due to state change this._battery_time_left_history = []; @@ -603,7 +645,7 @@ export const Sensors = GObject.registerClass({ _parseNvidiaSmiLine(callback, csv, gpuNum, multiGpu) { const expectedSplitLength = 19; - let csv_split = csv.split(','); + let csv_split = csv.split(',').map(value => value.trim()); // occasionally the nvidia-smi command can get cut off before it can be fully read, thus the parse function only gets part of a line // hence we count the number of bad splits and only terminate the process after a few bad splits in a row @@ -649,43 +691,63 @@ export const Sensors = GObject.registerClass({ const typeName = 'gpu#' + gpuNum; const globalLabel = 'GPU' + (multiGpu ? ' ' + gpuNum : ''); - const memTempValid = !isNaN(parseInt(temp_mem)); - - this._returnGpuValue(callback, 'Graphics', parseInt(util_gpu) * 0.01, typeName + '-group', 'percent'); + const fanSpeedPct = this._parseNumber(fan_speed_pct); + const tempGpu = this._parseNumber(temp_gpu); + const tempMem = this._parseNumber(temp_mem); + const memTotal = this._parseNumber(mem_total); + const memUsed = this._parseNumber(mem_used); + const memReserved = this._parseNumber(mem_reserved); + const memFree = this._parseNumber(mem_free); + const utilGpu = this._parseNumber(util_gpu); + const utilMem = this._parseNumber(util_mem); + const utilEncoder = this._parseNumber(util_encoder); + const utilDecoder = this._parseNumber(util_decoder); + const clockGpu = this._parseNumber(clock_gpu); + const clockMem = this._parseNumber(clock_mem); + const clockEncodeDecode = this._parseNumber(clock_encode_decode); + const powerDraw = this._parseNumber(power); + const averagePowerDraw = this._parseNumber(power_avg); + const tempLimit = this._parseNumber(staticInfo['temp_limit']); + const powerLimit = this._parseNumber(staticInfo['power_limit']); + const memTempValid = tempMem !== null; + + this._returnGpuValue(callback, 'Graphics', utilGpu !== null ? utilGpu * 0.01 : null, typeName + '-group', 'percent'); this._returnGpuValue(callback, 'Name', label, typeName, ''); - this._returnGpuValue(callback, globalLabel, parseInt(fan_speed_pct) * 0.01, 'fan', 'percent'); - this._returnGpuValue(callback, 'Fan', parseInt(fan_speed_pct) * 0.01, typeName, 'percent'); + this._returnGpuValue(callback, globalLabel, fanSpeedPct !== null ? fanSpeedPct * 0.01 : null, 'fan', 'percent'); + this._returnGpuValue(callback, 'Fan', fanSpeedPct !== null ? fanSpeedPct * 0.01 : null, typeName, 'percent'); - this._returnGpuValue(callback, globalLabel, parseInt(temp_gpu) * 1000, 'temperature', 'temp'); - this._returnGpuValue(callback, 'Temperature', parseInt(temp_gpu) * 1000, typeName, 'temp'); - this._returnGpuValue(callback, 'Memory Temperature', parseInt(temp_mem) * 1000, typeName, 'temp', memTempValid); - this._returnStaticGpuValue(callback, 'Temperature Limit', parseInt(staticInfo['temp_limit']) * 1000, typeName, 'temp'); + this._returnGpuValue(callback, globalLabel, tempGpu !== null ? tempGpu * 1000 : null, 'temperature', 'temp'); + this._returnGpuValue(callback, 'Temperature', tempGpu !== null ? tempGpu * 1000 : null, typeName, 'temp'); + this._returnGpuValue(callback, 'Memory Temperature', tempMem !== null ? tempMem * 1000 : null, typeName, 'temp', memTempValid); + this._returnStaticGpuValue(callback, 'Temperature Limit', tempLimit !== null ? tempLimit * 1000 : null, typeName, 'temp'); - this._returnGpuValue(callback, 'Memory Usage', parseInt(mem_used) / parseInt(mem_total), typeName, 'percent'); - this._returnGpuValue(callback, 'Memory Total', parseInt(mem_total) * 1000, typeName, 'memory'); - this._returnGpuValue(callback, 'Memory Used', parseInt(mem_used) * 1000, typeName, 'memory'); - this._returnGpuValue(callback, 'Memory Reserved', parseInt(mem_reserved) * 1000, typeName, 'memory'); - this._returnGpuValue(callback, 'Memory Free', parseInt(mem_free) * 1000, typeName, 'memory'); + this._returnGpuValue(callback, 'Memory Usage', memTotal > 0 && memUsed !== null ? memUsed / memTotal : null, typeName, 'percent'); + this._returnGpuValue(callback, 'Memory Total', memTotal !== null ? memTotal * 1000 : null, typeName, 'memory'); + this._returnGpuValue(callback, 'Memory Used', memUsed !== null ? memUsed * 1000 : null, typeName, 'memory'); + this._returnGpuValue(callback, 'Memory Reserved', memReserved !== null ? memReserved * 1000 : null, typeName, 'memory'); + this._returnGpuValue(callback, 'Memory Free', memFree !== null ? memFree * 1000 : null, typeName, 'memory'); - this._returnGpuValue(callback, 'Memory Utilization', parseInt(util_mem) * 0.01, typeName, 'percent'); - this._returnGpuValue(callback, 'Utilization', parseInt(util_gpu) * 0.01, typeName, 'percent'); - this._returnGpuValue(callback, 'Encoder Utilization', parseInt(util_encoder) * 0.01, typeName, 'percent'); - this._returnGpuValue(callback, 'Decoder Utilization', parseInt(util_decoder) * 0.01, typeName, 'percent'); + this._returnGpuValue(callback, 'Memory Utilization', utilMem !== null ? utilMem * 0.01 : null, typeName, 'percent'); + this._returnGpuValue(callback, 'Utilization', utilGpu !== null ? utilGpu * 0.01 : null, typeName, 'percent'); + this._returnGpuValue(callback, 'Encoder Utilization', utilEncoder !== null ? utilEncoder * 0.01 : null, typeName, 'percent'); + this._returnGpuValue(callback, 'Decoder Utilization', utilDecoder !== null ? utilDecoder * 0.01 : null, typeName, 'percent'); - this._returnGpuValue(callback, 'Frequency', parseInt(clock_gpu) * 1000 * 1000, typeName, 'hertz'); - this._returnGpuValue(callback, 'Memory Frequency', parseInt(clock_mem) * 1000 * 1000, typeName, 'hertz'); - this._returnGpuValue(callback, 'Encoder/Decoder Frequency', parseInt(clock_encode_decode) * 1000 * 1000, typeName, 'hertz'); + this._returnGpuValue(callback, 'Frequency', clockGpu !== null ? clockGpu * 1000 * 1000 : null, typeName, 'hertz'); + this._returnGpuValue(callback, 'Memory Frequency', clockMem !== null ? clockMem * 1000 * 1000 : null, typeName, 'hertz'); + this._returnGpuValue(callback, 'Encoder/Decoder Frequency', clockEncodeDecode !== null ? clockEncodeDecode * 1000 * 1000 : null, typeName, 'hertz'); //this._returnGpuValue(callback, 'Encoder Sessions', parseInt(encoder_sessions), typeName, 'string'); - this._returnGpuValue(callback, 'Power', power, typeName, 'watt-gpu'); - this._returnGpuValue(callback, 'Average Power', power_avg, typeName, 'watt-gpu'); - this._returnStaticGpuValue(callback, 'Power Limit', parseInt(staticInfo['power_limit']), typeName, 'watt-gpu'); + this._returnGpuValue(callback, 'Power', powerDraw, typeName, 'watt-gpu'); + this._returnGpuValue(callback, 'Average Power', averagePowerDraw, typeName, 'watt-gpu'); + this._returnStaticGpuValue(callback, 'Power Limit', powerLimit, typeName, 'watt-gpu'); - this._returnGpuValue(callback, 'Link Speed', link_gen_current + 'x' + link_width_current, typeName, 'pcie'); - this._returnStaticGpuValue(callback, 'Maximum Link Speed', staticInfo['link_gen_max'] + 'x' + staticInfo['link_width_max'], typeName, 'pcie'); + if (link_gen_current && link_width_current) + this._returnGpuValue(callback, 'Link Speed', link_gen_current + 'x' + link_width_current, typeName, 'pcie'); + if (staticInfo['link_gen_max'] && staticInfo['link_width_max']) + this._returnStaticGpuValue(callback, 'Maximum Link Speed', staticInfo['link_gen_max'] + 'x' + staticInfo['link_width_max'], typeName, 'pcie'); this._returnStaticGpuValue(callback, 'Addressing Mode', staticInfo['addressing_mode'], typeName, 'string'); @@ -761,7 +823,9 @@ export const Sensors = GObject.registerClass({ _returnGpuValue(callback, label, value, type, format, display = true) { if(!display) return; - if(format !== "string" && (value === 'N/A' || value === '[N/A]' || isNaN(value))) return; + if (!['', 'string', 'pcie'].includes(format) && + (value === 'N/A' || value === '[N/A]' || !Number.isFinite(value))) + return; let nvidiaLabel = {'label': label, 'type': type, 'format': format}; if (!this._nvidia_labels.includes(nvidiaLabel)) @@ -771,8 +835,9 @@ export const Sensors = GObject.registerClass({ } _returnValue(callback, label, value, type, format) { - // don't return if value is not a number - will revisit later - //if (isNaN(value)) return; + if (typeof value === 'number' && !Number.isFinite(value)) + return; + callback(label, value, type, format); } diff --git a/values.js b/values.js index 97ad2ab..cac2e51 100644 --- a/values.js +++ b/values.js @@ -420,8 +420,25 @@ export const Values = GObject.registerClass({ this._pushTimePoint('__' + type + '_ses__', sessionVal / memUnit, 'memory'); output.push(['Session ' + direction, this._legible(sum - this._networkSpeedOffset[key], format), type, '__' + type + '_ses__']); + if (!previousValue) { + if (!(direction in this._networkSpeeds)) this._networkSpeeds[direction] = {}; + this._networkSpeeds[direction][label] = 0; + + let sumNum = 0; + for (let iface in this._networkSpeeds[direction]) + sumNum += parseFloat(this._networkSpeeds[direction][iface]); + + let legibleSpeed = this._legible(sumNum, 'speed'); + output.push(['Device ' + direction, legibleSpeed, 'network-' + direction, '__network-' + direction + '_max__']); + if (direction == 'rx') + output.push([type, legibleSpeed, type + '-group', '']); + output.push([label, this._legible(0, 'speed'), type, key]); + return output; + } + // calculate speed for this interface - let speed = (value - previousValue[1]) / dwell; + let sampleDwell = dwell > 0 ? dwell : 1; + let speed = (value - previousValue[1]) / sampleDwell; output.push([label, this._legible(speed, 'speed'), type, key]); this._pushTimePoint(key, speed, 'speed');