Added custom wattd metric

This commit is contained in:
2025-10-26 04:41:34 +09:00
parent d9a7f6bb29
commit da08257bab
4 changed files with 134 additions and 0 deletions

View File

@@ -24,6 +24,7 @@ import HddtempUtil from './hddtempUtil.js';
import SmartctlUtil from './smartctlUtil.js'; import SmartctlUtil from './smartctlUtil.js';
import NvmecliUtil from './nvmecliUtil.js'; import NvmecliUtil from './nvmecliUtil.js';
import BatteryUtil from './batteryUtil.js'; import BatteryUtil from './batteryUtil.js';
import WattdUtil from './wattdUtil.js';
import FreonItem from './freonItem.js'; import FreonItem from './freonItem.js';
@@ -78,6 +79,7 @@ class FreonMenuButton extends PanelMenu.Button {
this._initFreeipmiUtility(); this._initFreeipmiUtility();
this._initLiquidctlUtility(); this._initLiquidctlUtility();
this._initBatteryUtility(); this._initBatteryUtility();
this._initWattdUtility();
this._initNvidiaUtility(); this._initNvidiaUtility();
this._initBumblebeeNvidiaUtility(); this._initBumblebeeNvidiaUtility();
@@ -142,6 +144,7 @@ class FreonMenuButton extends PanelMenu.Button {
this._addSettingChangedSignal('use-generic-lmsensors', this._sensorsUtilityChanged.bind(this)); this._addSettingChangedSignal('use-generic-lmsensors', this._sensorsUtilityChanged.bind(this));
this._addSettingChangedSignal('freeimpi-selected', this._freeipmiUtilityChanged.bind(this)); this._addSettingChangedSignal('freeimpi-selected', this._freeipmiUtilityChanged.bind(this));
this._addSettingChangedSignal('use-generic-liquidctl', this._liquidctlUtilityChanged.bind(this)); this._addSettingChangedSignal('use-generic-liquidctl', this._liquidctlUtilityChanged.bind(this));
this._addSettingChangedSignal('use-generic-wattd', this._wattdUtilityChanged.bind(this));
this._addSettingChangedSignal('show-battery-stats', this._batteryUtilityChanged.bind(this)); this._addSettingChangedSignal('show-battery-stats', this._batteryUtilityChanged.bind(this));
this._addSettingChangedSignal('use-gpu-nvidia', this._nvidiaUtilityChanged.bind(this)); this._addSettingChangedSignal('use-gpu-nvidia', this._nvidiaUtilityChanged.bind(this));
@@ -364,6 +367,25 @@ class FreonMenuButton extends PanelMenu.Button {
this._updateUI(true); this._updateUI(true);
} }
_initWattdUtility() {
if (this._settings.get_boolean('use-generic-wattd'))
this._utils.wattd = new WattdUtil();
}
_destroyWattdUtility() {
if (this._utils.wattd) {
this._utils.wattd.destroy();
delete this._utils.wattd;
}
}
_wattdUtilityChanged() {
this._destroyWattdUtility();
this._initWattdUtility();
this._querySensors();
this._updateUI(true);
}
_initNvidiaUtility() { _initNvidiaUtility() {
if (this._settings.get_boolean('use-gpu-nvidia')) if (this._settings.get_boolean('use-gpu-nvidia'))
this._utils.nvidia = new NvidiaUtil(); this._utils.nvidia = new NvidiaUtil();
@@ -531,6 +553,7 @@ class FreonMenuButton extends PanelMenu.Button {
this._destroyNvmecliUtility(); this._destroyNvmecliUtility();
this._destroyBatteryUtility(); this._destroyBatteryUtility();
this._destroyWattdUtility();
GLib.Source.remove(this._timeoutId); GLib.Source.remove(this._timeoutId);
GLib.Source.remove(this._updateUITimeoutId); GLib.Source.remove(this._updateUITimeoutId);
@@ -640,6 +663,10 @@ class FreonMenuButton extends PanelMenu.Button {
} }
} }
if (this._utils.wattd && this._utils.wattd.available)
if (this._settings.get_boolean('show-power'))
powerInfo = powerInfo.concat(this._utils.wattd.power);
if (this._utils.nvidia && this._utils.nvidia.available) if (this._utils.nvidia && this._utils.nvidia.available)
if (this._settings.get_boolean('show-temperature')) if (this._settings.get_boolean('show-temperature'))
gpuTempInfo = gpuTempInfo.concat(this._utils.nvidia.temp); gpuTempInfo = gpuTempInfo.concat(this._utils.nvidia.temp);

View File

@@ -112,6 +112,7 @@ export default class FreonPreferences extends ExtensionPreferences {
group.add(this._addSwitch("lm-sensors", "use-generic-lmsensors", "Read sensors from lm-sensors")); group.add(this._addSwitch("lm-sensors", "use-generic-lmsensors", "Read sensors from lm-sensors"));
group.add(this._addSwitch("liquidctl", "use-generic-liquidctl", "Read sensors from liquidctl (v1.7.0+)")); group.add(this._addSwitch("liquidctl", "use-generic-liquidctl", "Read sensors from liquidctl (v1.7.0+)"));
group.add(this._addSwitch("wattd", "use-generic-wattd", "Read power data from wattd"));
const freeimpi = new Adw.ComboRow({ const freeimpi = new Adw.ComboRow({
title: _('FreeIMPI'), title: _('FreeIMPI'),

View File

@@ -105,6 +105,12 @@
<description>Read sensors from (requires liquidctl v1.7.0 or later)</description> <description>Read sensors from (requires liquidctl v1.7.0 or later)</description>
</key> </key>
<key type="b" name="use-generic-wattd">
<default>false</default>
<summary>Read power data from wattd</summary>
<description>Read power sensors exposed by wattd</description>
</key>
<key type="b" name="use-gpu-nvidia"> <key type="b" name="use-gpu-nvidia">
<default>false</default> <default>false</default>
<summary>Read GPU sensors from NVIDIA driver</summary> <summary>Read GPU sensors from NVIDIA driver</summary>

View File

@@ -0,0 +1,100 @@
import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
export default class WattdUtil {
constructor() {
this._powerDir = '/run/power';
this._readings = [];
this._updated = false;
this._available = GLib.file_test(this._powerDir, GLib.FileTest.IS_DIR);
}
get available() {
return this._available;
}
get updated() {
return this._updated;
}
set updated(updated) {
this._updated = updated;
}
execute(callback) {
this._updated = false;
this._readings = [];
try {
this._available = GLib.file_test(this._powerDir, GLib.FileTest.IS_DIR);
if (!this._available) {
return;
}
const directory = Gio.File.new_for_path(this._powerDir);
let enumerator = null;
try {
enumerator = directory.enumerate_children('standard::name,standard::type', Gio.FileQueryInfoFlags.NONE, null);
let info;
while ((info = enumerator.next_file(null)) !== null) {
if (info.get_file_type() !== Gio.FileType.REGULAR)
continue;
const name = info.get_name();
const file = directory.get_child(name);
try {
const [ok, contents] = file.load_contents(null);
if (!ok)
continue;
const value = parseFloat(new TextDecoder('utf-8').decode(contents).trim());
if (isNaN(value))
continue;
const feature = {
label: this._formatLabel(name),
power: Math.abs(value),
};
this._readings.push(feature);
} catch (e) {
logError(e, `[FREON] Failed to read wattd metric ${name}`);
}
}
} finally {
if (enumerator !== null)
enumerator.close(null);
}
this._readings.sort((a, b) => a.label.localeCompare(b.label, undefined, {numeric: true}));
} catch (e) {
logError(e, '[FREON] Failed to enumerate wattd metrics');
this._available = false;
this._readings = [];
} finally {
this._updated = true;
if (callback)
callback();
}
}
get power() {
return this._readings;
}
destroy() {
this._readings = [];
}
_formatLabel(name) {
const parts = name.split(/[-_]/).filter(part => part.length > 0);
if (parts.length === 0)
return name;
return parts.map(part => part[0].toUpperCase() + part.slice(1)).join(' ');
}
}