diff --git a/bumblebee/modules/cpu2.py b/bumblebee/modules/cpu2.py new file mode 100644 index 0000000..d7e83b5 --- /dev/null +++ b/bumblebee/modules/cpu2.py @@ -0,0 +1,157 @@ +"""Multiwidget CPU module + +Can display any combination of: + + * max CPU frequency + * total CPU load in percents (integer value) + * per-core CPU load as graph - either mono or colored + * CPU temperature (in Celsius degrees) + * CPU fan speed + +Requirements: + + * the psutil Python module for the first three items from the list above + * sensors executable for the rest + +Parameters: + * cpu2.layout: Space-separated list of widgets to add. + Possible widgets are: + * cpu2.maxfreq + * cpu2.cpuload + * cpu2.coresload + * cpu2.temp + * cpu2.fanspeed + * cpu2.colored: 1 for colored per core load graph, 0 for mono (default) + if this is set to 1, use --markup=pango + * cpu2.temp_pattern: pattern to look for in the output of 'sensors -u'; + required if cpu2.temp widged is used + * cpu2.fan_pattern: pattern to look for in the output of 'sensors -u'; + required if cpu2.fanspeed widged is used + +Note: if you are getting "n/a" for CPU temperature / fan speed, then you're +lacking the aforementioned pattern settings or they have wrong values. + +""" + +try: + import psutil +except ImportError: + pass + +import bumblebee.engine +import bumblebee.util +import bumblebee.output + + +class Module(bumblebee.engine.Module): + + def __init__(self, engine, config): + super(Module, self).__init__(engine, config, []) + self._layout = self.parameter("layout", "cpu2.maxfreq cpu2.cpuload cpu2.coresload cpu2.temp cpu2.fanspeed") + self._widget_names = self._layout.split() + widget_list = [] + for widget_name in self._widget_names: + if widget_name == "cpu2.maxfreq": + widget = bumblebee.output.Widget( + name=widget_name, full_text=self.maxfreq) + widget.set("type", "freq") + elif widget_name == "cpu2.cpuload": + widget = bumblebee.output.Widget( + name=widget_name, full_text=self.cpuload) + widget.set("type", "load") + elif widget_name == "cpu2.coresload": + widget = bumblebee.output.Widget( + name=widget_name, full_text=self.coresload) + widget.set("type", "loads") + elif widget_name == "cpu2.temp": + widget = bumblebee.output.Widget( + name=widget_name, full_text=self.temp) + widget.set("type", "temp") + elif widget_name == "cpu2.fanspeed": + widget = bumblebee.output.Widget( + name=widget_name, full_text=self.fanspeed) + widget.set("type", "fan") + widget_list.append(widget) + self.widgets(widget_list) + self._colored = bumblebee.util.asbool(self.parameter("colored", 0)) + self._temp_pattern = self.parameter("temp_pattern") + if self._temp_pattern is None: + self._temp = "n/a" + self._fan_pattern = self.parameter("fan_pattern") + if self._fan_pattern is None: + self._fan = "n/a" + # maxfreq is loaded only once at startup + if "cpu2.maxfreq" in self._widget_names: + self._maxfreq = psutil.cpu_freq().max / 1000 + self.update(widget_list) + + def maxfreq(self, _): + return "{:.2f}GHz".format(self._maxfreq) + + def cpuload(self, _): + return "{}%".format(self._cpuload) + + @staticmethod + def add_color(bar): + """add color as pango markup to a bar""" + if bar in ["▁", "▂"]: + color = "green" + elif bar in ["▃", "▄"]: + color = "yellow" + elif bar in ["▅", "▆"]: + color = "orange" + elif bar in ["▇", "█"]: + color = "red" + colored_bar = "{}".format(color, bar) + return colored_bar + + def coresload(self, _): + mono_bars = [bumblebee.output.hbar(x) for x in self._coresload] + if not self._colored: + return "".join(mono_bars) + colored_bars = [self.add_color(x) for x in mono_bars] + return "".join(colored_bars) + + def temp(self, _): + if self._temp == "n/a" or self._temp == 0: + return "n/a" + return "{}°C".format(self._temp) + + def fanspeed(self, _): + if self._fanspeed == "n/a": + return "n/a" + return "{}RPM".format(self._fanspeed) + + def _parse_sensors_output(self): + output = bumblebee.util.execute("sensors -u") + lines = output.split("\n") + temp = "n/a" + fan = "n/a" + temp_line = None + fan_line = None + for line in lines: + if self._temp_pattern is not None and self._temp_pattern in line: + temp_line = line + if self._fan_pattern is not None and self._fan_pattern in line: + fan_line = line + if temp_line is not None and fan_line is not None: + break + if temp_line is not None: + temp = round(float(temp_line.split(":")[1].strip())) + if fan_line is not None: + fan = int(fan_line.split(":")[1].strip()[:-4]) + return temp, fan + + def update(self, _): + if "cpu2.maxfreq" in self._widget_names: + self._maxfreq = psutil.cpu_freq().max / 1000 + if "cpu2.cpuload" in self._widget_names: + self._cpuload = round(psutil.cpu_percent(percpu=False)) + if "cpu2.coresload" in self._widget_names: + self._coresload = psutil.cpu_percent(percpu=True) + if "cpu2.temp" in self._widget_names or "cpu2.fanspeed" in self._widget_names: + self._temp, self._fanspeed = self._parse_sensors_output() + + def state(self, widget): + """for having per-widget icons""" + return [widget.get("type", "")] diff --git a/themes/icons/ascii.json b/themes/icons/ascii.json index 73c6651..01aff79 100644 --- a/themes/icons/ascii.json +++ b/themes/icons/ascii.json @@ -8,6 +8,13 @@ "cpu": { "prefix": "cpu" }, + "cpu2": { + "freq": { "prefix": "freq" }, + "load": { "prefix": "load" }, + "loads": { "prefix": "" }, + "temp": { "prefix": "temp" }, + "fan": { "prefix": "fan" } + }, "disk": { "prefix": "hdd" }, diff --git a/themes/icons/awesome-fonts.json b/themes/icons/awesome-fonts.json index 4da5365..8c6688c 100644 --- a/themes/icons/awesome-fonts.json +++ b/themes/icons/awesome-fonts.json @@ -12,6 +12,13 @@ "datetimetz": { "prefix": "" }, "memory": { "prefix": "" }, "cpu": { "prefix": "" }, + "cpu2": { + "freq": { "prefix": "" }, + "load": { "prefix": "" }, + "loads": { "prefix": "" }, + "temp": { "prefix": "" }, + "fan": { "prefix": "" } + }, "disk": { "prefix": "" }, "dnf": { "prefix": "" }, "apt": { "prefix": "" },