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(' ');
+ }
+}