diff --git a/modules/contrib/cpu2.py b/modules/contrib/cpu2.py new file mode 100644 index 0000000..250ec4d --- /dev/null +++ b/modules/contrib/cpu2.py @@ -0,0 +1,156 @@ +"""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 "{:>3}%".format(self._cpuload) + + def add_color(self, bar): + """add color as pango markup to a bar""" + if bar in ["▁", "▂"]: + color = self.theme().color("green", "green") + elif bar in ["▃", "▄"]: + color = self.theme().color("yellow", "yellow") + elif bar in ["▅", "▆"]: + color = self.theme().color("orange", "orange") + elif bar in ["▇", "█"]: + color = self.theme().color("red", "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", "")]