From da08257bab1470f28205d0c060ed62ca6225f528 Mon Sep 17 00:00:00 2001 From: Morgan Date: Sun, 26 Oct 2025 04:41:34 +0900 Subject: [PATCH] Added custom `wattd` metric --- .../extension.js | 27 +++++ .../prefs.js | 1 + ...gnome.shell.extensions.sensors.gschema.xml | 6 ++ .../wattdUtil.js | 100 ++++++++++++++++++ 4 files changed, 134 insertions(+) create mode 100644 freon@UshakovVasilii_Github.yahoo.com/wattdUtil.js diff --git a/freon@UshakovVasilii_Github.yahoo.com/extension.js b/freon@UshakovVasilii_Github.yahoo.com/extension.js index cdaaa92..25d48ef 100644 --- a/freon@UshakovVasilii_Github.yahoo.com/extension.js +++ b/freon@UshakovVasilii_Github.yahoo.com/extension.js @@ -24,6 +24,7 @@ import HddtempUtil from './hddtempUtil.js'; import SmartctlUtil from './smartctlUtil.js'; import NvmecliUtil from './nvmecliUtil.js'; import BatteryUtil from './batteryUtil.js'; +import WattdUtil from './wattdUtil.js'; import FreonItem from './freonItem.js'; @@ -78,6 +79,7 @@ class FreonMenuButton extends PanelMenu.Button { this._initFreeipmiUtility(); this._initLiquidctlUtility(); this._initBatteryUtility(); + this._initWattdUtility(); this._initNvidiaUtility(); this._initBumblebeeNvidiaUtility(); @@ -142,6 +144,7 @@ class FreonMenuButton extends PanelMenu.Button { this._addSettingChangedSignal('use-generic-lmsensors', this._sensorsUtilityChanged.bind(this)); this._addSettingChangedSignal('freeimpi-selected', this._freeipmiUtilityChanged.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('use-gpu-nvidia', this._nvidiaUtilityChanged.bind(this)); @@ -364,6 +367,25 @@ class FreonMenuButton extends PanelMenu.Button { 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() { if (this._settings.get_boolean('use-gpu-nvidia')) this._utils.nvidia = new NvidiaUtil(); @@ -531,6 +553,7 @@ class FreonMenuButton extends PanelMenu.Button { this._destroyNvmecliUtility(); this._destroyBatteryUtility(); + this._destroyWattdUtility(); GLib.Source.remove(this._timeoutId); 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._settings.get_boolean('show-temperature')) gpuTempInfo = gpuTempInfo.concat(this._utils.nvidia.temp); diff --git a/freon@UshakovVasilii_Github.yahoo.com/prefs.js b/freon@UshakovVasilii_Github.yahoo.com/prefs.js index 68faf04..1f6adda 100644 --- a/freon@UshakovVasilii_Github.yahoo.com/prefs.js +++ b/freon@UshakovVasilii_Github.yahoo.com/prefs.js @@ -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("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({ title: _('FreeIMPI'), diff --git a/freon@UshakovVasilii_Github.yahoo.com/schemas/org.gnome.shell.extensions.sensors.gschema.xml b/freon@UshakovVasilii_Github.yahoo.com/schemas/org.gnome.shell.extensions.sensors.gschema.xml index 83536dc..7e30d68 100644 --- a/freon@UshakovVasilii_Github.yahoo.com/schemas/org.gnome.shell.extensions.sensors.gschema.xml +++ b/freon@UshakovVasilii_Github.yahoo.com/schemas/org.gnome.shell.extensions.sensors.gschema.xml @@ -105,6 +105,12 @@ Read sensors from (requires liquidctl v1.7.0 or later) + + false + Read power data from wattd + Read power sensors exposed by wattd + + false Read GPU sensors from NVIDIA driver diff --git a/freon@UshakovVasilii_Github.yahoo.com/wattdUtil.js b/freon@UshakovVasilii_Github.yahoo.com/wattdUtil.js new file mode 100644 index 0000000..b3a0de6 --- /dev/null +++ b/freon@UshakovVasilii_Github.yahoo.com/wattdUtil.js @@ -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(' '); + } +}