From 7756eaaa31523111df898e4af506c66b4b43c0f4 Mon Sep 17 00:00:00 2001 From: jslabik Date: Sat, 20 Mar 2021 01:18:46 +0100 Subject: [PATCH 001/301] Adding the ability to change the editor to module todo --- bumblebee_status/modules/contrib/todo.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bumblebee_status/modules/contrib/todo.py b/bumblebee_status/modules/contrib/todo.py index 878b63f..df2d788 100644 --- a/bumblebee_status/modules/contrib/todo.py +++ b/bumblebee_status/modules/contrib/todo.py @@ -21,9 +21,10 @@ class Module(core.module.Module): super().__init__(config, theme, core.widget.Widget(self.output)) self.__doc = os.path.expanduser(self.parameter("file", "~/Documents/todo.txt")) + self.__editor = self.parameter("editor", "xdg-open") self.__todos = self.count_items() core.input.register( - self, button=core.input.LEFT_MOUSE, cmd="xdg-open {}".format(self.__doc) + self, button=core.input.LEFT_MOUSE, cmd="{} {}".format(self.__editor, self.__doc) ) def output(self, widget): From 10c9321c24ff0f0ca1056d5c34ed74c57e0fc14b Mon Sep 17 00:00:00 2001 From: Marcos Gabriel Miller Date: Sat, 27 Mar 2021 17:22:10 -0300 Subject: [PATCH 002/301] [themes] add albiceleste-powerline --- themes/albiceleste-powerline.json | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 themes/albiceleste-powerline.json diff --git a/themes/albiceleste-powerline.json b/themes/albiceleste-powerline.json new file mode 100644 index 0000000..e046882 --- /dev/null +++ b/themes/albiceleste-powerline.json @@ -0,0 +1,20 @@ +{ + "icons": [ "awesome-fonts" ], + "defaults": { + "separator-block-width": 0, + "warning": { + "fg": "#000000", + "bg": "#ffff00" + }, + "critical": { + "fg": "#000000", + "bg": "#ffff00" + + } + }, + "cycle": [ + {"fg": "#000000", "bg": "#87ceeb"}, + {"fg": "#000000", "bg": "#FFFFFF"} + ] + +} From abcf861fcbfbaaf1d615f154dfcfa75e938bd377 Mon Sep 17 00:00:00 2001 From: Marcos Gabriel Miller Date: Sat, 27 Mar 2021 17:22:35 -0300 Subject: [PATCH 003/301] [themes] add rastafari-powerline --- themes/rastafari-powerline.json | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 themes/rastafari-powerline.json diff --git a/themes/rastafari-powerline.json b/themes/rastafari-powerline.json new file mode 100644 index 0000000..b7c8c21 --- /dev/null +++ b/themes/rastafari-powerline.json @@ -0,0 +1,21 @@ +{ + "icons": [ "awesome-fonts" ], + "defaults": { + "separator-block-width": 0, + "warning": { + "fg": "#000000", + "bg": "#FFFFFF" + }, + "critical": { + "fg": "#000000", + "bg": "#FFFFFF" + } + }, + "cycle": [ + {"fg": "#FFFFFF", "bg": "#008000"}, + {"fg": "#000000", "bg": "#ffff00"}, + {"fg": "#000000", "bg": "#ff0000"} + + ] + +} \ No newline at end of file From 527d1706c2b842bdebf9a83224dc76619d7f6ffc Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Fri, 2 Apr 2021 03:30:09 +0000 Subject: [PATCH 004/301] [core/module] add fallback for module loading Looks like some Python versions work with find_spec(), others with spec_from_file_location(), so add find_spec() as fallback. fixes #779 --- bumblebee_status/core/module.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/bumblebee_status/core/module.py b/bumblebee_status/core/module.py index 5be7a4f..c4b66a4 100644 --- a/bumblebee_status/core/module.py +++ b/bumblebee_status/core/module.py @@ -27,10 +27,16 @@ def import_user(module_short, config, theme): return getattr(mod, "Module")(config, theme) else: log.debug("importing {} from user via importlib.util".format(module_short)) - spec = importlib.util.spec_from_file_location("modules.{}".format(module_short), usermod) - mod = importlib.util.module_from_spec(spec) - spec.loader.exec_module(mod) - return mod.Module(config, theme) + try: + spec = importlib.util.spec_from_file_location("modules.{}".format(module_short), usermod) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + return mod.Module(config, theme) + except Exception as e: + spec = importlib.util.find_spec("modules.{}".format(module_short), usermod) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + return mod.Module(config, theme) raise ImportError("not found") """Loads a module by name From 0410ac9c6b2860a0bb077d384f8da07ba37d15ce Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Sat, 3 Apr 2021 19:24:01 +0000 Subject: [PATCH 005/301] [doc/shortcut] better example for shortcut module --- bumblebee_status/modules/contrib/shortcut.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bumblebee_status/modules/contrib/shortcut.py b/bumblebee_status/modules/contrib/shortcut.py index 129bfdc..ff5b47f 100644 --- a/bumblebee_status/modules/contrib/shortcut.py +++ b/bumblebee_status/modules/contrib/shortcut.py @@ -9,7 +9,7 @@ a delimiter (; semicolon by default). For example in order to create two shortcuts labeled A and B with commands cmdA and cmdB you could do: - ./bumblebee-status -m shortcut -p shortcut.cmd='ls;ps' shortcut.label='A;B' + ./bumblebee-status -m shortcut -p shortcut.cmd='firefox https://www.google.com;google-chrome https://google.com' shortcut.label='Google (Firefox);Google (Chrome)' Parameters: * shortcut.cmds : List of commands to execute From 4a6be622a88668cab02790191c312323a848fbbe Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Sat, 3 Apr 2021 19:29:40 +0000 Subject: [PATCH 006/301] [modules/rotation] fix widget creation each iteration of the rotation module created new/duplicate widgets, causing a status bar of infinite length. fixes #782 --- bumblebee_status/modules/contrib/rotation.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bumblebee_status/modules/contrib/rotation.py b/bumblebee_status/modules/contrib/rotation.py index 13f656e..d05c463 100644 --- a/bumblebee_status/modules/contrib/rotation.py +++ b/bumblebee_status/modules/contrib/rotation.py @@ -31,14 +31,13 @@ class Module(core.module.Module): orientation = curr_orient break - widget = self.widget(display) + widget = self.widget(name=display) if not widget: widget = self.add_widget(full_text=display, name=display) core.input.register( widget, button=core.input.LEFT_MOUSE, cmd=self.__toggle ) widget.set("orientation", orientation) - widgets.append(widget) def state(self, widget): return widget.get("orientation", "normal") From 8001ed3ada13eb9799442a258d7c1a5f17633029 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Tue, 27 Apr 2021 17:16:54 +0200 Subject: [PATCH 007/301] [modules/cpu] Add per-cpu mode By explicitly setting "cpu.percpu=True" as parameter, it is now possible to get the CPU utilization on a per-cpu basis. Future extension: One widget per CPU (add ID of CPU and add warning/error state on a per CPU basis) see #785 --- bumblebee_status/modules/core/cpu.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/bumblebee_status/modules/core/cpu.py b/bumblebee_status/modules/core/cpu.py index 59c6e71..51dc695 100644 --- a/bumblebee_status/modules/core/cpu.py +++ b/bumblebee_status/modules/core/cpu.py @@ -12,6 +12,7 @@ Parameters: * cpu.warning : Warning threshold in % of CPU usage (defaults to 70%) * cpu.critical: Critical threshold in % of CPU usage (defaults to 80%) * cpu.format : Format string (defaults to '{:.01f}%') + * cpu.percpu : If set to true, show each individual cpu (defaults to false) """ import psutil @@ -20,28 +21,37 @@ import core.module import core.widget import core.input +import util.format + class Module(core.module.Module): def __init__(self, config, theme): super().__init__(config, theme, core.widget.Widget(self.utilization)) - self.widget().set("theme.minwidth", self._format.format(100.0 - 10e-20)) - self._utilization = psutil.cpu_percent(percpu=False) + self._percpu = util.format.asbool(self.parameter("percpu", False)) + self.update() + self.widget().set("theme.minwidth", self._format.format(*[100.0 - 10e-20]*len(self._utilization))) core.input.register( self, button=core.input.LEFT_MOUSE, cmd="gnome-system-monitor" ) @property def _format(self): - return self.parameter("format", "{:.01f}%") + fmt = self.parameter("format", "{:.01f}%") + if self._percpu: + fmt = [fmt]*len(self._utilization) + fmt = " ".join(fmt) + return fmt def utilization(self, _): - return self._format.format(self._utilization) + return self._format.format(*self._utilization) def update(self): - self._utilization = psutil.cpu_percent(percpu=False) + self._utilization = psutil.cpu_percent(percpu=self._percpu) + if not self._percpu: + self._utilization = [self._utilization] def state(self, _): - return self.threshold_state(self._utilization, 70, 80) + return self.threshold_state(sum(self._utilization)/len(self._utilization), 70, 80) # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From 10c169af8a7035d1d7bf450adede183bbfab8f3c Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Tue, 27 Apr 2021 17:17:13 +0200 Subject: [PATCH 008/301] [modules/core/cpu] optionally add per-cpu widget if the parameter "percpu" is set to true, create one widget per cpu, and also handle warning/error state on a per-widget basis. see #785 --- bumblebee_status/modules/core/cpu.py | 33 +++++++++++++++------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/bumblebee_status/modules/core/cpu.py b/bumblebee_status/modules/core/cpu.py index 51dc695..77ac20a 100644 --- a/bumblebee_status/modules/core/cpu.py +++ b/bumblebee_status/modules/core/cpu.py @@ -26,32 +26,35 @@ import util.format class Module(core.module.Module): def __init__(self, config, theme): - super().__init__(config, theme, core.widget.Widget(self.utilization)) + super().__init__(config, theme, []) self._percpu = util.format.asbool(self.parameter("percpu", False)) - self.update() - self.widget().set("theme.minwidth", self._format.format(*[100.0 - 10e-20]*len(self._utilization))) + + for idx, cpu_perc in enumerate(self.cpu_utilization()): + widget = self.add_widget(name="cpu#{}".format(idx), full_text=self.utilization) + widget.set("utilization", cpu_perc) + widget.set("theme.minwidth", self._format.format(100.0 - 10e-20)) + core.input.register( self, button=core.input.LEFT_MOUSE, cmd="gnome-system-monitor" ) @property def _format(self): - fmt = self.parameter("format", "{:.01f}%") - if self._percpu: - fmt = [fmt]*len(self._utilization) - fmt = " ".join(fmt) - return fmt + return self.parameter("format", "{:.01f}%") - def utilization(self, _): - return self._format.format(*self._utilization) + def utilization(self, widget): + return self._format.format(widget.get("utilization", 0.0)) + + def cpu_utilization(self): + tmp = psutil.cpu_percent(percpu=self._percpu) + return tmp if self._percpu else [tmp] def update(self): - self._utilization = psutil.cpu_percent(percpu=self._percpu) - if not self._percpu: - self._utilization = [self._utilization] + for idx, cpu_perc in enumerate(self.cpu_utilization()): + self.widgets()[idx].set("utilization", cpu_perc) - def state(self, _): - return self.threshold_state(sum(self._utilization)/len(self._utilization), 70, 80) + def state(self, widget): + return self.threshold_state(widget.get("utilization", 0.0), 70, 80) # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From fb6be007e590a48fbcb8ef623943118323b56ddf Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Tue, 27 Apr 2021 17:17:28 +0200 Subject: [PATCH 009/301] [core/output] fix minimum width with padding when calculating the minimum width of a widget, also take the padding into consideration. see #785 --- bumblebee_status/core/output.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/bumblebee_status/core/output.py b/bumblebee_status/core/output.py index 1760309..9c032d6 100644 --- a/bumblebee_status/core/output.py +++ b/bumblebee_status/core/output.py @@ -57,6 +57,9 @@ class block(object): def set(self, key, value): self.__attributes[key] = value + def get(self, key, default=None): + return self.__attributes.get(key, default) + def is_pango(self, attr): if isinstance(attr, dict) and "pango" in attr: return True @@ -91,9 +94,17 @@ class block(object): assign(self.__attributes, result, "background", "bg") if "full_text" in self.__attributes: + prefix = self.__pad(self.pangoize(self.__attributes.get("prefix"))) + suffix = self.__pad(self.pangoize(self.__attributes.get("suffix"))) + self.set("_prefix", prefix) + self.set("_suffix", suffix) + self.set("_raw", self.get("full_text")) result["full_text"] = self.pangoize(result["full_text"]) result["full_text"] = self.__format(self.__attributes["full_text"]) + if "min-width" in self.__attributes and "padding" in self.__attributes: + self.set("min-width", self.__format(self.get("min-width"))) + for k in [ "name", "instance", @@ -123,11 +134,8 @@ class block(object): def __format(self, text): if text is None: return None - prefix = self.__pad(self.pangoize(self.__attributes.get("prefix"))) - suffix = self.__pad(self.pangoize(self.__attributes.get("suffix"))) - self.set("_prefix", prefix) - self.set("_suffix", suffix) - self.set("_raw", text) + prefix = self.get("_prefix") + suffix = self.get("_suffix") return "{}{}{}".format(prefix, text, suffix) From 028932a560a87f12829324c2565be6664cce1e26 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Tue, 27 Apr 2021 17:17:55 +0200 Subject: [PATCH 010/301] [tests/cpu] adapt tests and add per-cpu tests fixes #785 --- tests/modules/core/test_cpu.py | 37 +++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/tests/modules/core/test_cpu.py b/tests/modules/core/test_cpu.py index eca2362..d1a015e 100644 --- a/tests/modules/core/test_cpu.py +++ b/tests/modules/core/test_cpu.py @@ -7,8 +7,9 @@ import modules.core.cpu pytest.importorskip("psutil") -def build_module(): - config = core.config.Config([]) +def build_module(percpu=False): + config = core.config.Config(["-p", "percpu={}".format(percpu)]) + config.set("cpu.percpu", percpu) return modules.core.cpu.Module(config=config, theme=None) def cpu_widget(module): @@ -42,21 +43,47 @@ class TestCPU(TestCase): cpu_percent_mock.return_value = 50 module = build_module() - assert module.state(None) == None + assert module.state(module.widget()) == None @mock.patch('psutil.cpu_percent') def test_warning_state(self, cpu_percent_mock): cpu_percent_mock.return_value = 75 module = build_module() - assert module.state(None) == 'warning' + assert module.state(module.widget()) == 'warning' @mock.patch('psutil.cpu_percent') def test_critical_state(self, cpu_percent_mock): cpu_percent_mock.return_value = 82 module = build_module() - assert module.state(None) == 'critical' + assert module.state(module.widget()) == 'critical' + + @mock.patch('psutil.cpu_percent') + def test_healthy_state_percpu(self, cpu_percent_mock): + cpu_percent_mock.return_value = [50,42,47] + module = build_module(percpu=True) + + for widget in module.widgets(): + assert module.state(widget) == None + + @mock.patch('psutil.cpu_percent') + def test_warning_state_percpu(self, cpu_percent_mock): + cpu_percent_mock.return_value = [50,72,47] + module = build_module(percpu=True) + + assert module.state(module.widgets()[0]) == None + assert module.state(module.widgets()[1]) == "warning" + assert module.state(module.widgets()[2]) == None + + @mock.patch('psutil.cpu_percent') + def test_warning_state_percpu(self, cpu_percent_mock): + cpu_percent_mock.return_value = [50,72,99] + module = build_module(percpu=True) + + assert module.state(module.widgets()[0]) == None + assert module.state(module.widgets()[1]) == "warning" + assert module.state(module.widgets()[2]) == "critical" @mock.patch('core.input.register') def test_register_left_mouse_action(self, input_register_mock): From 1e13798c958e2ddbd33aba70dfed09204ab5f925 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Wed, 28 Apr 2021 12:41:04 +0200 Subject: [PATCH 011/301] [core/input] add pseudo-event "update" to selectively update modules to trigger an update of a module (without actually triggering a mouse interaction), use the special event "update": bumblebee-ctl -m -b update see #784 --- bumblebee-ctl | 3 ++- bumblebee_status/core/input.py | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/bumblebee-ctl b/bumblebee-ctl index b1c0f37..435162d 100755 --- a/bumblebee-ctl +++ b/bumblebee-ctl @@ -12,6 +12,7 @@ button = { "right-mouse": 3, "wheel-up": 4, "wheel-down": 5, + "update": -1, } @@ -20,7 +21,7 @@ def main(): parser.add_argument( "-b", "--button", - choices=["left-mouse", "right-mouse", "middle-mouse", "wheel-up", "wheel-down"], + choices=["left-mouse", "right-mouse", "middle-mouse", "wheel-up", "wheel-down", "update"], help="button to emulate", default="left-mouse", ) diff --git a/bumblebee_status/core/input.py b/bumblebee_status/core/input.py index b0d9f23..2f9fdfc 100644 --- a/bumblebee_status/core/input.py +++ b/bumblebee_status/core/input.py @@ -10,6 +10,7 @@ MIDDLE_MOUSE = 2 RIGHT_MOUSE = 3 WHEEL_UP = 4 WHEEL_DOWN = 5 +UPDATE = -1 def button_name(button): @@ -23,6 +24,8 @@ def button_name(button): return "wheel-up" if button == WHEEL_DOWN: return "wheel-down" + if button == UPDATE: + return "update" return "n/a" From 9553bec7db7113dd82c443d57a6a722a222e6d07 Mon Sep 17 00:00:00 2001 From: Edward <57063825+solar-core@users.noreply.github.com> Date: Wed, 28 Apr 2021 20:48:49 +0300 Subject: [PATCH 012/301] Update modules.rst --- docs/modules.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/modules.rst b/docs/modules.rst index 87a80ad..440a0a3 100644 --- a/docs/modules.rst +++ b/docs/modules.rst @@ -22,6 +22,7 @@ Parameters: * cpu.warning : Warning threshold in % of CPU usage (defaults to 70%) * cpu.critical: Critical threshold in % of CPU usage (defaults to 80%) * cpu.format : Format string (defaults to '{:.01f}%') + * cpu.percpu : If set to true, show each individual cpu (defaults to false) .. image:: ../screenshots/cpu.png From 046b950b8a0c9c5bd91b40b1d5b1ceeb4f7ef939 Mon Sep 17 00:00:00 2001 From: Frederic Junod Date: Mon, 10 May 2021 14:35:34 +0200 Subject: [PATCH 013/301] Fix parameters name for the sun module --- bumblebee_status/modules/contrib/sun.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bumblebee_status/modules/contrib/sun.py b/bumblebee_status/modules/contrib/sun.py index 6b0734d..e9eefd2 100644 --- a/bumblebee_status/modules/contrib/sun.py +++ b/bumblebee_status/modules/contrib/sun.py @@ -8,8 +8,8 @@ Requires the following python packages: * python-dateutil Parameters: - * cpu.lat : Latitude of your location - * cpu.lon : Longitude of your location + * sun.lat : Latitude of your location + * sun.lon : Longitude of your location (if none of those are set, location is determined automatically via location APIs) From 7f03c9ce2d075ca42385fd8c5a9ed1804ecda211 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Mon, 10 May 2021 17:48:23 +0000 Subject: [PATCH 014/301] [doc] update module documentation --- docs/modules.rst | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/modules.rst b/docs/modules.rst index 440a0a3..0e166e6 100644 --- a/docs/modules.rst +++ b/docs/modules.rst @@ -85,6 +85,14 @@ Requires: .. image:: ../screenshots/git.png +keys +~~~~ + +Shows when a key is pressed + +Parameters: + * keys.keys: Comma-separated list of keys to monitor (defaults to "") + layout-xkb ~~~~~~~~~~ @@ -1238,7 +1246,7 @@ a delimiter (; semicolon by default). For example in order to create two shortcuts labeled A and B with commands cmdA and cmdB you could do: - ./bumblebee-status -m shortcut -p shortcut.cmd='ls;ps' shortcut.label='A;B' + ./bumblebee-status -m shortcut -p shortcut.cmd='firefox https://www.google.com;google-chrome https://google.com' shortcut.label='Google (Firefox);Google (Chrome)' Parameters: * shortcut.cmds : List of commands to execute @@ -1321,9 +1329,6 @@ stock Display a stock quote from finance.yahoo.com -Requires the following python packages: - * requests - Parameters: * stock.symbols : Comma-separated list of symbols to fetch * stock.change : Should we fetch change in stock value (defaults to True) @@ -1344,8 +1349,8 @@ Requires the following python packages: * python-dateutil Parameters: - * cpu.lat : Latitude of your location - * cpu.lon : Longitude of your location + * sun.lat : Latitude of your location + * sun.lon : Longitude of your location (if none of those are set, location is determined automatically via location APIs) From 902288f30d5f5cdcf619950ebc5acc1976486f72 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Tue, 11 May 2021 11:23:06 +0200 Subject: [PATCH 015/301] [modules/sensors] do not truncate temperature use strip() instead of a sub-list to get the value for the temperature. fixes #787 --- bumblebee_status/modules/contrib/sensors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bumblebee_status/modules/contrib/sensors.py b/bumblebee_status/modules/contrib/sensors.py index 68b792a..8164f00 100644 --- a/bumblebee_status/modules/contrib/sensors.py +++ b/bumblebee_status/modules/contrib/sensors.py @@ -98,7 +98,7 @@ class Module(core.module.Module): try: temperature = open( self.parameter("path", "/sys/class/thermal/thermal_zone0/temp") - ).read()[:2] + ).read().strip() log.debug("retrieved temperature from /sys/class/") # TODO: Iterate through all thermal zones to determine the correct one and use its value # https://unix.stackexchange.com/questions/304845/discrepancy-between-number-of-cores-and-thermal-zones-in-sys-class-thermal From dfd23a44de8964dcd4ae9e1971c24c6efed71664 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Sun, 16 May 2021 21:09:58 +0200 Subject: [PATCH 016/301] [modules/layout] add a new - generic - layout module Add a new module "layout" that will eventually evolve into the only keyboard layout module. Right now, it uses an external binary (get-kbd-layout) to determine the layout of a keyboard device (because I did not manage to call libX11 with ctypes correctly). see #788 see #790 --- .gitignore | 2 ++ bin/get-kbd-layout | Bin 0 -> 21512 bytes bumblebee_status/modules/core/layout.py | 39 ++++++++++++++++++++++++ util/Makefile | 13 ++++++++ util/layout.c | 39 ++++++++++++++++++++++++ 5 files changed, 93 insertions(+) create mode 100755 bin/get-kbd-layout create mode 100644 bumblebee_status/modules/core/layout.py create mode 100644 util/Makefile create mode 100644 util/layout.c diff --git a/.gitignore b/.gitignore index 2bf1ac8..c21fe43 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +*.o + # Vim swap files *swp *~ diff --git a/bin/get-kbd-layout b/bin/get-kbd-layout new file mode 100755 index 0000000000000000000000000000000000000000..7ce6ccf1f0f105b58ed302f5950b540abfcf4a6f GIT binary patch literal 21512 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 +#include + +#include + +void err_if(int condition, const char* msg) +{ + if (condition) { + fprintf(stderr, "fatal: %s\n", msg); + exit(1); + } +} + +int main(int argc, char** argv) +{ + Display* display = XOpenDisplay(NULL); + err_if(!display, "unable to open display"); + + int kbd = argc == 1 ? XkbUseCoreKbd : atoi(argv[1]); + + XkbStateRec state; + XkbGetState(display, kbd, &state); + + XkbDescPtr desc = XkbGetKeyboard(display, XkbAllComponentsMask, kbd); + char* symbols = XGetAtomName(display, desc->names->symbols); + printf("%s\n", symbols); + +#if 0 + char *group = XGetAtomName(display, desc->names->groups[state.group]); + XFree(group); +#endif + XFree(symbols); + XFree(desc); + + XCloseDisplay(display); + + return 0; +} + From 4b6b4b905217893a27bb5e336114ea78fa43e799 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Mon, 24 May 2021 12:56:02 +0200 Subject: [PATCH 017/301] [core] add custom minimizer capability Add a new set of parameters to allow modules to be customly minimized. It works like this: If a module has the parameter "minimize" set to a true value, it will *not* use the built-in minimizer, and instead look for "minimized" parameters (e.g. if date has the "format" parameter, it would look for "minimized.format" when in minimized state). This allows the user to have different parametrization for different states. Also, using the "start-minimized" parameter allows for modules to start minimized. Note: This is hinging off the *module*, not the *widget* (the current, hard-coded hiding is per-widget). This means that modules using this method will only show a single widget - the first one - when in minimized state. The module author has to account for that. see #791 --- bumblebee_status/core/module.py | 4 ++++ bumblebee_status/core/output.py | 10 ++++++++++ bumblebee_status/modules/core/datetime.py | 4 ++-- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/bumblebee_status/core/module.py b/bumblebee_status/core/module.py index c4b66a4..312862d 100644 --- a/bumblebee_status/core/module.py +++ b/bumblebee_status/core/module.py @@ -95,6 +95,8 @@ class Module(core.input.Object): self.alias = self.__config.get("__alias__", None) self.id = self.alias if self.alias else self.name self.next_update = None + self.minimized = False + self.minimized = self.parameter("start-minimized", False) self.theme = theme @@ -126,6 +128,8 @@ class Module(core.input.Object): for prefix in [self.name, self.module_name, self.alias]: value = self.__config.get("{}.{}".format(prefix, key), value) + if self.minimized: + value = self.__config.get("{}.minimized.{}".format(prefix, key), value) return value """Set a parameter for this module diff --git a/bumblebee_status/core/output.py b/bumblebee_status/core/output.py index 9c032d6..121df27 100644 --- a/bumblebee_status/core/output.py +++ b/bumblebee_status/core/output.py @@ -166,6 +166,12 @@ class i3(object): def toggle_minimize(self, event): widget_id = event["instance"] + for module in self.__modules: + if module.widget(widget_id=widget_id) and util.format.asbool(module.parameter("minimize", False)) == True: + # this module can customly minimize + module.minimized = not module.minimized + return + if widget_id in self.__content: self.__content[widget_id]["minimized"] = not self.__content[widget_id]["minimized"] @@ -216,6 +222,10 @@ class i3(object): def blocks(self, module): blocks = [] + if module.minimized: + blocks.extend(self.separator_block(module, module.widgets()[0])) + blocks.append(self.__content_block(module, module.widgets()[0])) + return blocks for widget in module.widgets(): if widget.module and self.__config.autohide(widget.module.name): if not any( diff --git a/bumblebee_status/modules/core/datetime.py b/bumblebee_status/modules/core/datetime.py index f421e15..febb5fb 100644 --- a/bumblebee_status/modules/core/datetime.py +++ b/bumblebee_status/modules/core/datetime.py @@ -21,7 +21,6 @@ class Module(core.module.Module): super().__init__(config, theme, core.widget.Widget(self.full_text)) core.input.register(self, button=core.input.LEFT_MOUSE, cmd="calendar") - self._fmt = self.parameter("format", self.default_format()) l = locale.getdefaultlocale() if not l or l == (None, None): l = ("en_US", "UTF-8") @@ -36,7 +35,8 @@ class Module(core.module.Module): def full_text(self, widget): enc = locale.getpreferredencoding() - retval = datetime.datetime.now().strftime(self._fmt) + fmt = self.parameter("format", self.default_format()) + retval = datetime.datetime.now().strftime(fmt) if hasattr(retval, "decode"): return retval.decode(enc) return retval From 51f68addcdb3dbf266a89c1510935ad230eacb5a Mon Sep 17 00:00:00 2001 From: Yufan You Date: Fri, 11 Jun 2021 17:34:46 +0800 Subject: [PATCH 018/301] [modules/playerctl]: BREAKING: use `playerctl -f` and add `playerctl.args` 1. Use `playerctl -f` to format, which is more powerful. This also fixes #767, which is caused by missing a few fields of the metadata. 2. Add `playerctl.args`, so that users can choose a specific player, etc. 3. Display nothing when there's no running player. This is a breaking change. Users need to change `{title}` to `{{title}}`. --- bumblebee_status/modules/contrib/playerctl.py | 65 +++++++++---------- docs/modules.rst | 8 ++- 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/bumblebee_status/modules/contrib/playerctl.py b/bumblebee_status/modules/contrib/playerctl.py index 3a0d7e5..d370df4 100755 --- a/bumblebee_status/modules/contrib/playerctl.py +++ b/bumblebee_status/modules/contrib/playerctl.py @@ -6,12 +6,14 @@ Requires the following executable: * playerctl Parameters: - * playerctl.format: Format string (defaults to '{artist} - {title}') - Available values are: {album}, {title}, {artist}, {trackNumber} + * playerctl.format: Format string (defaults to '{{artist}} - {{title}} {{duration(position)}}/{{duration(mpris:length)}}') + The format string is passed to 'playerctl -f' as an argument. See the 'Format Strings' section of 'man playerctl' for more information. * playerctl.layout: Comma-separated list to change order of widgets (defaults to song, previous, pause, next) Widget names are: playerctl.song, playerctl.prev, playerctl.pause, playerctl.next + * playerctl.args: The arguments added to playerctl. + You can check 'playerctl --help' or `its README `_. For example, it could be '-p vlc,%any'. -Parameters are inherited from `spotify` module, many thanks to its developers! +Parameters are inspired by the `spotify` module, many thanks to its developers! contributed by `smitajit `_ - many thanks! """ @@ -36,9 +38,8 @@ class Module(core.module.Module): ) ) - self.__song = "" - self.__cmd = "playerctl " - self.__format = self.parameter("format", "{artist} - {title}") + self.__cmd = "playerctl " + self.parameter("args", "") + " " + self.__format = self.parameter("format", "{{artist}} - {{title}} {{duration(position)}}/{{duration(mpris:length)}}") widget_map = {} for widget_name in self.__layout: @@ -48,7 +49,6 @@ class Module(core.module.Module): "button": core.input.LEFT_MOUSE, "cmd": self.__cmd + "previous", } - widget.set("state", "prev") elif widget_name == "playerctl.pause": widget_map[widget] = { "button": core.input.LEFT_MOUSE, @@ -59,7 +59,6 @@ class Module(core.module.Module): "button": core.input.LEFT_MOUSE, "cmd": self.__cmd + "next", } - widget.set("state", "next") elif widget_name == "playerctl.song": widget_map[widget] = [ { @@ -86,32 +85,32 @@ class Module(core.module.Module): def update(self): try: - self.__get_song() - - for widget in self.widgets(): - if widget.name == "playerctl.pause": - playback_status = str(util.cli.execute(self.__cmd + "status")).strip() - if playback_status != "": - if playback_status == "Playing": - widget.set("state", "playing") - else: - widget.set("state", "paused") - elif widget.name == "playerctl.song": - widget.set("state", "song") - widget.full_text(self.__song) + playback_status = str(util.cli.execute(self.__cmd + "status")).strip() except Exception as e: logging.exception(e) - self.__song = "" + playback_status = None + for widget in self.widgets(): + if playback_status: + if widget.name == "playerctl.pause": + if playback_status == "Playing": + widget.set("state", "playing") + elif playback_status == "Paused": + widget.set("state", "paused") + else: + widget.set("state", "") + elif widget.name == "playerctl.next": + widget.set("state", "next") + elif widget.name == "playerctl.prev": + widget.set("state", "prev") + elif widget.name == "playerctl.song": + widget.full_text(self.__get_song()) + else: + widget.set("state", "") + widget.full_text(" ") def __get_song(self): - album = str(util.cli.execute(self.__cmd + "metadata xesam:album")).strip() - title = str(util.cli.execute(self.__cmd + "metadata xesam:title")).strip() - artist = str(util.cli.execute(self.__cmd + "metadata xesam:albumArtist")).strip() - track_number = str(util.cli.execute(self.__cmd + "metadata xesam:trackNumber")).strip() - - self.__song = self.__format.format( - album = album, - title = title, - artist = artist, - trackNumber = track_number - ) + try: + return str(util.cli.execute(self.__cmd + "metadata -f '" + self.__format + "'")).strip() + except Exception as e: + logging.exception(e) + return " " diff --git a/docs/modules.rst b/docs/modules.rst index 0e166e6..25952d8 100644 --- a/docs/modules.rst +++ b/docs/modules.rst @@ -1060,12 +1060,14 @@ Requires the following executable: * playerctl Parameters: - * playerctl.format: Format string (defaults to '{artist} - {title}') - Available values are: {album}, {title}, {artist}, {trackNumber} + * playerctl.format: Format string (defaults to '{{artist}} - {{title}} {{duration(position)}}/{{duration(mpris:length)}}') + The format string is passed to 'playerctl -f' as an argument. See the 'Format Strings' section of 'man playerctl' for more information. * playerctl.layout: Comma-separated list to change order of widgets (defaults to song, previous, pause, next) Widget names are: playerctl.song, playerctl.prev, playerctl.pause, playerctl.next + * playerctl.args: The arguments added to playerctl. + You can check 'playerctl --help' or `its readme `_. For example, it could be '-p vlc,%any'. -Parameters are inherited from `spotify` module, many thanks to its developers! +Parameters are inspired by the `spotify` module, many thanks to its developers! contributed by `smitajit `_ - many thanks! From c4046d0cd22f3d238d63dd751cd7dd625a7d361e Mon Sep 17 00:00:00 2001 From: Yufan You Date: Fri, 11 Jun 2021 18:12:13 +0800 Subject: [PATCH 019/301] [doc]: link to the README instead of manpage --- bumblebee_status/modules/contrib/playerctl.py | 4 ++-- docs/modules.rst | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bumblebee_status/modules/contrib/playerctl.py b/bumblebee_status/modules/contrib/playerctl.py index d370df4..ad3cfc0 100755 --- a/bumblebee_status/modules/contrib/playerctl.py +++ b/bumblebee_status/modules/contrib/playerctl.py @@ -6,8 +6,8 @@ Requires the following executable: * playerctl Parameters: - * playerctl.format: Format string (defaults to '{{artist}} - {{title}} {{duration(position)}}/{{duration(mpris:length)}}') - The format string is passed to 'playerctl -f' as an argument. See the 'Format Strings' section of 'man playerctl' for more information. + * playerctl.format: Format string (defaults to '{{artist}} - {{title}} {{duration(position)}}/{{duration(mpris:length)}}'). + The format string is passed to 'playerctl -f' as an argument. Read `the README `_ for more information. * playerctl.layout: Comma-separated list to change order of widgets (defaults to song, previous, pause, next) Widget names are: playerctl.song, playerctl.prev, playerctl.pause, playerctl.next * playerctl.args: The arguments added to playerctl. diff --git a/docs/modules.rst b/docs/modules.rst index 25952d8..49e509b 100644 --- a/docs/modules.rst +++ b/docs/modules.rst @@ -1060,8 +1060,8 @@ Requires the following executable: * playerctl Parameters: - * playerctl.format: Format string (defaults to '{{artist}} - {{title}} {{duration(position)}}/{{duration(mpris:length)}}') - The format string is passed to 'playerctl -f' as an argument. See the 'Format Strings' section of 'man playerctl' for more information. + * playerctl.format: Format string (defaults to '{{artist}} - {{title}} {{duration(position)}}/{{duration(mpris:length)}}'). + The format string is passed to 'playerctl -f' as an argument. Read `the README `_ for more information. * playerctl.layout: Comma-separated list to change order of widgets (defaults to song, previous, pause, next) Widget names are: playerctl.song, playerctl.prev, playerctl.pause, playerctl.next * playerctl.args: The arguments added to playerctl. From e5007a5729fdf6cdd743b247cb0ab65613f90656 Mon Sep 17 00:00:00 2001 From: Sayan Date: Thu, 24 Jun 2021 23:17:35 +0530 Subject: [PATCH 020/301] Add active gpu module using optimus-manager --- bumblebee_status/modules/contrib/optman.py | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 bumblebee_status/modules/contrib/optman.py diff --git a/bumblebee_status/modules/contrib/optman.py b/bumblebee_status/modules/contrib/optman.py new file mode 100644 index 0000000..c928f24 --- /dev/null +++ b/bumblebee_status/modules/contrib/optman.py @@ -0,0 +1,36 @@ +"""Displays currently active gpu by optimus-manager +Requires the following packages: + + * optimus-manager + +""" + +import subprocess + +import core.module +import core.widget + + +class Module(core.module.Module): + def __init__(self, config, theme): + super().__init__(config, theme, core.widget.Widget(self.output)) + self.__gpumode = "" + + def output(self, _): + return "GPU: {}".format(self.__gpumode) + + def update(self): + cmd = ["optimus-manager", "--print-mode"] + output = ( + subprocess.Popen(cmd, stdout=subprocess.PIPE) + .communicate()[0] + .decode("utf-8") + .lower() + ) + + if "intel" in output: + self.__gpumode = "Intel" + elif "nvidia" in output: + self.__gpumode = "Nvidia" + elif "amd" in output: + self.__gpumode = "AMD" From 37ccbd7f4a1e7aa359de4b437eaf58f035673ed9 Mon Sep 17 00:00:00 2001 From: Yufan You Date: Sat, 26 Jun 2021 18:19:12 +0800 Subject: [PATCH 021/301] [modules/playerctl]: support the stopped status --- bumblebee_status/modules/contrib/playerctl.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bumblebee_status/modules/contrib/playerctl.py b/bumblebee_status/modules/contrib/playerctl.py index ad3cfc0..e02fa84 100755 --- a/bumblebee_status/modules/contrib/playerctl.py +++ b/bumblebee_status/modules/contrib/playerctl.py @@ -96,6 +96,8 @@ class Module(core.module.Module): widget.set("state", "playing") elif playback_status == "Paused": widget.set("state", "paused") + elif playback_status == "Stopped": + widget.set("state", "stopped") else: widget.set("state", "") elif widget.name == "playerctl.next": From 4485b65722645d6c9617b5ff4aea6d62ee8a9adf Mon Sep 17 00:00:00 2001 From: Sayan Sil Date: Wed, 30 Jun 2021 11:31:42 +0530 Subject: [PATCH 022/301] Use the existing util.cli module --- bumblebee_status/modules/contrib/optman.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/bumblebee_status/modules/contrib/optman.py b/bumblebee_status/modules/contrib/optman.py index c928f24..337003c 100644 --- a/bumblebee_status/modules/contrib/optman.py +++ b/bumblebee_status/modules/contrib/optman.py @@ -5,11 +5,10 @@ Requires the following packages: """ -import subprocess - import core.module import core.widget +import util.cli class Module(core.module.Module): def __init__(self, config, theme): @@ -20,13 +19,8 @@ class Module(core.module.Module): return "GPU: {}".format(self.__gpumode) def update(self): - cmd = ["optimus-manager", "--print-mode"] - output = ( - subprocess.Popen(cmd, stdout=subprocess.PIPE) - .communicate()[0] - .decode("utf-8") - .lower() - ) + cmd = "optimus-manager --print-mode" + output = util.cli.execute(cmd).strip() if "intel" in output: self.__gpumode = "Intel" From 1232c4d96092be16c76a9baac35a8c4bb4868683 Mon Sep 17 00:00:00 2001 From: nepoz Date: Mon, 5 Jul 2021 07:55:47 -0500 Subject: [PATCH 023/301] Initial commit -- give basic message about interface being used --- bumblebee_status/modules/contrib/network.py | 48 +++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 bumblebee_status/modules/contrib/network.py diff --git a/bumblebee_status/modules/contrib/network.py b/bumblebee_status/modules/contrib/network.py new file mode 100644 index 0000000..37115e1 --- /dev/null +++ b/bumblebee_status/modules/contrib/network.py @@ -0,0 +1,48 @@ +""" +A module to show currently active network connection (ethernet or wifi) +and connection strength. +""" + +import subprocess +import os + +import core.module +import core.widget + + +class Module(core.module.Module): + def __init__(self, config, theme): + super().__init__(config, theme, core.widget.Widget(self.network)) + self._is_wireless = True + self._interface = None + self._message = None + + def network(self, widgets): + # start subprocess to get networked data + std_out = os.popen("ip route get 8.8.8.8") + route_str = " ".join(std_out.read().split()) + route_tokens = route_str.split(" ") + + try: + self._interface = route_tokens[route_tokens.index("dev") + 1] + ":" + except ValueError: + self._interface = None + + with open("/proc/net/wireless", "r") as f: + if self._interface: + self._is_wireless = self._interface in f.read() + + # setup message to send to bar + if self._interface is None: + self._message = "Not connected to a network" + elif self._is_wireless: + self._message = "Connected to WiFi" + else: + # self._message = "Connected to Ethernet" + self._message = self._message + + + return self._message + + + From f141b95d8f7dd53e86b1e9aff3ce9b12a0178be8 Mon Sep 17 00:00:00 2001 From: nepoz Date: Mon, 5 Jul 2021 10:29:37 -0500 Subject: [PATCH 024/301] Basic functionaly for dealingn with signal strength --- bumblebee_status/modules/contrib/network.py | 49 ++++++++++++++++----- 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/bumblebee_status/modules/contrib/network.py b/bumblebee_status/modules/contrib/network.py index 37115e1..c40d0d8 100644 --- a/bumblebee_status/modules/contrib/network.py +++ b/bumblebee_status/modules/contrib/network.py @@ -3,8 +3,9 @@ A module to show currently active network connection (ethernet or wifi) and connection strength. """ -import subprocess -import os + +import util.cli +import util.format import core.module import core.widget @@ -18,31 +19,55 @@ class Module(core.module.Module): self._message = None def network(self, widgets): - # start subprocess to get networked data - std_out = os.popen("ip route get 8.8.8.8") - route_str = " ".join(std_out.read().split()) + # run ip route command, tokenize output + cmd = "ip route get 8.8.8.8" + std_out = util.cli.execute(cmd) + route_str = " ".join(std_out.split()) route_tokens = route_str.split(" ") + # Attempt to extract a valid network interface device try: - self._interface = route_tokens[route_tokens.index("dev") + 1] + ":" + self._interface = route_tokens[route_tokens.index("dev") + 1] except ValueError: self._interface = None - with open("/proc/net/wireless", "r") as f: - if self._interface: + # Check to see if the interface (if it exists) is wireless + if self._interface: + with open("/proc/net/wireless", "r") as f: self._is_wireless = self._interface in f.read() + f.close() - # setup message to send to bar + # setup message to send to the user if self._interface is None: self._message = "Not connected to a network" elif self._is_wireless: - self._message = "Connected to WiFi" + cmd = "iwgetid" + iw_dat = util.cli.execute(cmd) + has_ssid = "ESSID" in iw_dat + ssid = iw_dat[iw_dat.index(":") + 2: -2] if has_ssid else "Unknown" + + # Get connection strength + cmd = "iwconfig {}".format(self._interface) + config_dat = " ".join(util.cli.execute(cmd).split()) + config_tokens = config_dat.replace("=", " ").split() + strength = config_tokens[config_tokens.index("level") + 1] + strength = util.format.asint(strength, minimum=-110, maximum=-30) + + self._message = self.__generate_wireless_message(ssid, strength) else: - # self._message = "Connected to Ethernet" self._message = self._message - return self._message + def __generate_wireless_message(self, ssid, strength): + computed_strength = 100 * (strength + 110) / 70.0 + if computed_strength < 25: + return ssid + " poor" + if computed_strength < 50: + return ssid + " fair" + if computed_strength < 75: + return ssid + " good" + + return ssid + " excellent" From 4987c7d3e2fa1152224c3d7d540f56dbfdf315dc Mon Sep 17 00:00:00 2001 From: nepoz Date: Mon, 5 Jul 2021 11:26:46 -0500 Subject: [PATCH 025/301] added stateful behavior --- bumblebee_status/modules/contrib/network.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/bumblebee_status/modules/contrib/network.py b/bumblebee_status/modules/contrib/network.py index c40d0d8..30601b5 100644 --- a/bumblebee_status/modules/contrib/network.py +++ b/bumblebee_status/modules/contrib/network.py @@ -17,6 +17,7 @@ class Module(core.module.Module): self._is_wireless = True self._interface = None self._message = None + self.__signal = -110 def network(self, widgets): # run ip route command, tokenize output @@ -51,19 +52,27 @@ class Module(core.module.Module): config_dat = " ".join(util.cli.execute(cmd).split()) config_tokens = config_dat.replace("=", " ").split() strength = config_tokens[config_tokens.index("level") + 1] - strength = util.format.asint(strength, minimum=-110, maximum=-30) + self.__signal = util.format.asint(strength, minimum=-110, maximum=-30) - self._message = self.__generate_wireless_message(ssid, strength) + self._message = self.__generate_wireless_message(ssid, self.__signal) else: self._message = self._message return self._message + def state(self, widget): + if self.__signal < -65: + return "warning" + if self.__signal < -80: + return "critical" + return None + + def __generate_wireless_message(self, ssid, strength): - computed_strength = 100 * (strength + 110) / 70.0 - if computed_strength < 25: - return ssid + " poor" + computed_strength = 100 * ((strength + 100) / 70.0) + if computed_strength < 30: + return ssid + " poor" if computed_strength < 50: return ssid + " fair" if computed_strength < 75: From 448ab6de836b0eedcc7250c0aa97104f3df0c696 Mon Sep 17 00:00:00 2001 From: nepoz Date: Mon, 5 Jul 2021 12:34:42 -0500 Subject: [PATCH 026/301] Functional display for wireless connection --- bumblebee_status/modules/contrib/network.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/bumblebee_status/modules/contrib/network.py b/bumblebee_status/modules/contrib/network.py index 30601b5..f91073e 100644 --- a/bumblebee_status/modules/contrib/network.py +++ b/bumblebee_status/modules/contrib/network.py @@ -9,9 +9,11 @@ import util.format import core.module import core.widget +import core.input class Module(core.module.Module): + @core.decorators.every(seconds=10) def __init__(self, config, theme): super().__init__(config, theme, core.widget.Widget(self.network)) self._is_wireless = True @@ -19,6 +21,9 @@ class Module(core.module.Module): self._message = None self.__signal = -110 + # Set up event handler for left mouse click + core.input.register(self, button=core.input.LEFT_MOUSE, cmd="nm-connection-editor") + def network(self, widgets): # run ip route command, tokenize output cmd = "ip route get 8.8.8.8" @@ -71,12 +76,7 @@ class Module(core.module.Module): def __generate_wireless_message(self, ssid, strength): computed_strength = 100 * ((strength + 100) / 70.0) - if computed_strength < 30: - return ssid + " poor" - if computed_strength < 50: - return ssid + " fair" - if computed_strength < 75: - return ssid + " good" + return " {} {}%".format(ssid, int(computed_strength)) + - return ssid + " excellent" From c7df1926dce8fdf3fb0427d79e802955a60694d6 Mon Sep 17 00:00:00 2001 From: nepoz Date: Mon, 5 Jul 2021 13:09:17 -0500 Subject: [PATCH 027/301] Formatting fixes, fixed state management and added some icons --- bumblebee_status/modules/contrib/network.py | 36 +++++++++++---------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/bumblebee_status/modules/contrib/network.py b/bumblebee_status/modules/contrib/network.py index f91073e..924c7f4 100644 --- a/bumblebee_status/modules/contrib/network.py +++ b/bumblebee_status/modules/contrib/network.py @@ -16,9 +16,9 @@ class Module(core.module.Module): @core.decorators.every(seconds=10) def __init__(self, config, theme): super().__init__(config, theme, core.widget.Widget(self.network)) - self._is_wireless = True - self._interface = None - self._message = None + self.__is_wireless = True + self.__interface = None + self.__message = None self.__signal = -110 # Set up event handler for left mouse click @@ -33,48 +33,50 @@ class Module(core.module.Module): # Attempt to extract a valid network interface device try: - self._interface = route_tokens[route_tokens.index("dev") + 1] + self.__interface = route_tokens[route_tokens.index("dev") + 1] except ValueError: - self._interface = None + self.__interface = None # Check to see if the interface (if it exists) is wireless - if self._interface: + if self.__interface: with open("/proc/net/wireless", "r") as f: - self._is_wireless = self._interface in f.read() + self.__is_wireless = self.__interface in f.read() f.close() # setup message to send to the user - if self._interface is None: - self._message = "Not connected to a network" - elif self._is_wireless: + if self.__interface is None: + self.__message = " No connection" + elif self.__is_wireless: cmd = "iwgetid" iw_dat = util.cli.execute(cmd) has_ssid = "ESSID" in iw_dat ssid = iw_dat[iw_dat.index(":") + 2: -2] if has_ssid else "Unknown" # Get connection strength - cmd = "iwconfig {}".format(self._interface) + cmd = "iwconfig {}".format(self.__interface) config_dat = " ".join(util.cli.execute(cmd).split()) config_tokens = config_dat.replace("=", " ").split() strength = config_tokens[config_tokens.index("level") + 1] self.__signal = util.format.asint(strength, minimum=-110, maximum=-30) - self._message = self.__generate_wireless_message(ssid, self.__signal) + self.__message = self.__generate_wireles_message(ssid, self.__signal) else: - self._message = self._message + self.__signal = -30 + self.__message = " Ethernet" - return self._message + return self.__message def state(self, widget): - if self.__signal < -65: - return "warning" if self.__signal < -80: return "critical" + if self.__signal < -65: + return "warning" + return None - def __generate_wireless_message(self, ssid, strength): + def __generate_wireles_message(self, ssid, strength): computed_strength = 100 * ((strength + 100) / 70.0) return " {} {}%".format(ssid, int(computed_strength)) From 911230c65998ac8969eba88507c09b8de463373e Mon Sep 17 00:00:00 2001 From: nepoz Date: Mon, 5 Jul 2021 13:54:28 -0500 Subject: [PATCH 028/301] first complete implementation of the network module --- bumblebee_status/modules/contrib/network.py | 34 ++++++++++++--------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/bumblebee_status/modules/contrib/network.py b/bumblebee_status/modules/contrib/network.py index 924c7f4..0c366d6 100644 --- a/bumblebee_status/modules/contrib/network.py +++ b/bumblebee_status/modules/contrib/network.py @@ -1,6 +1,8 @@ """ -A module to show currently active network connection (ethernet or wifi) -and connection strength. +A module to show currently active network connection (ethernet or wifi) and connection strength if the connection is wireless. + +Dependencies: nm-connection-editor if users would like a graphical +network manager when left-clicking the module """ @@ -16,14 +18,17 @@ class Module(core.module.Module): @core.decorators.every(seconds=10) def __init__(self, config, theme): super().__init__(config, theme, core.widget.Widget(self.network)) - self.__is_wireless = True + self.__is_wireless = False self.__interface = None self.__message = None self.__signal = -110 # Set up event handler for left mouse click - core.input.register(self, button=core.input.LEFT_MOUSE, cmd="nm-connection-editor") + core.input.register( + self, button=core.input.LEFT_MOUSE, cmd="nm-connection-editor" + ) + # Get network information to display to the user def network(self, widgets): # run ip route command, tokenize output cmd = "ip route get 8.8.8.8" @@ -33,7 +38,7 @@ class Module(core.module.Module): # Attempt to extract a valid network interface device try: - self.__interface = route_tokens[route_tokens.index("dev") + 1] + self.__interface = route_tokens[route_tokens.index("dev") + 1] except ValueError: self.__interface = None @@ -47,10 +52,13 @@ class Module(core.module.Module): if self.__interface is None: self.__message = " No connection" elif self.__is_wireless: - cmd = "iwgetid" - iw_dat = util.cli.execute(cmd) + iw_dat = util.cli.execute("iwgetid") has_ssid = "ESSID" in iw_dat - ssid = iw_dat[iw_dat.index(":") + 2: -2] if has_ssid else "Unknown" + ssid = ( + iw_dat[iw_dat.index(":") + 1 :].replace('"', "").strip() + if has_ssid + else "Unknown" + ) # Get connection strength cmd = "iwconfig {}".format(self.__interface) @@ -61,12 +69,13 @@ class Module(core.module.Module): self.__message = self.__generate_wireles_message(ssid, self.__signal) else: + # Set signal to -30 as ethernet shouldn't have signal issues self.__signal = -30 - self.__message = " Ethernet" + self.__message = " Ethernet" return self.__message - + # The signal is measured in decibels/milliwatt, hence the weird numbers def state(self, widget): if self.__signal < -80: return "critical" @@ -75,10 +84,7 @@ class Module(core.module.Module): return None - + # manually done for better granularity / ease of parsing strength data def __generate_wireles_message(self, ssid, strength): computed_strength = 100 * ((strength + 100) / 70.0) return " {} {}%".format(ssid, int(computed_strength)) - - - From 3f524ab371a1780619914c12fbb7d87836a57f92 Mon Sep 17 00:00:00 2001 From: nepoz Date: Thu, 8 Jul 2021 09:04:40 -0500 Subject: [PATCH 029/301] Refactoring, making use of netifaces --- bumblebee_status/modules/contrib/network.py | 82 +++++++++++---------- 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/bumblebee_status/modules/contrib/network.py b/bumblebee_status/modules/contrib/network.py index 0c366d6..d90a21b 100644 --- a/bumblebee_status/modules/contrib/network.py +++ b/bumblebee_status/modules/contrib/network.py @@ -13,78 +13,84 @@ import core.module import core.widget import core.input +import netifaces +import socket + class Module(core.module.Module): @core.decorators.every(seconds=10) def __init__(self, config, theme): super().__init__(config, theme, core.widget.Widget(self.network)) self.__is_wireless = False + self.__is_connected = False self.__interface = None self.__message = None self.__signal = -110 - # Set up event handler for left mouse click - core.input.register( - self, button=core.input.LEFT_MOUSE, cmd="nm-connection-editor" - ) - # Get network information to display to the user def network(self, widgets): - # run ip route command, tokenize output - cmd = "ip route get 8.8.8.8" - std_out = util.cli.execute(cmd) - route_str = " ".join(std_out.split()) - route_tokens = route_str.split(" ") + # Determine whether there is an internet connection + try: + socket.create_connection(("1.1.1.1", 53)) + self.__is_connected = True + except OSError: + self.__is_connected = False # Attempt to extract a valid network interface device - try: - self.__interface = route_tokens[route_tokens.index("dev") + 1] - except ValueError: - self.__interface = None + self.__interface = netifaces.gateways()["default"][netifaces.AF_INET][1] - # Check to see if the interface (if it exists) is wireless - if self.__interface: + # Check to see if the interface (if connected to the internet) is wireless + if self.__is_connected: with open("/proc/net/wireless", "r") as f: self.__is_wireless = self.__interface in f.read() - f.close() + f.close() # setup message to send to the user - if self.__interface is None: - self.__message = " No connection" - elif self.__is_wireless: + if not self.__is_connected: + self.__message = "No connection" + elif not self.__is_wireless: + # Assuming that if user is connected via non-wireless means that it will be ethernet + self.__signal = -30 + self.__message = "Ethernet" + else: + # We have a wireless connection iw_dat = util.cli.execute("iwgetid") has_ssid = "ESSID" in iw_dat + signal = self.__compute_signal(self.__interface) + self.__signal = util.format.asint(signal, minimum=-110, maximum=-30) + ssid = ( iw_dat[iw_dat.index(":") + 1 :].replace('"', "").strip() if has_ssid else "Unknown" ) - - # Get connection strength - cmd = "iwconfig {}".format(self.__interface) - config_dat = " ".join(util.cli.execute(cmd).split()) - config_tokens = config_dat.replace("=", " ").split() - strength = config_tokens[config_tokens.index("level") + 1] - self.__signal = util.format.asint(strength, minimum=-110, maximum=-30) - self.__message = self.__generate_wireles_message(ssid, self.__signal) - else: - # Set signal to -30 as ethernet shouldn't have signal issues - self.__signal = -30 - self.__message = " Ethernet" return self.__message - # The signal is measured in decibels/milliwatt, hence the weird numbers + # State determined by signal strength def state(self, widget): - if self.__signal < -80: + if self.__compute_strength(self.__signal) < 50: return "critical" - if self.__signal < -65: + if self.__compute_strength(self.__signal) < 75: return "warning" return None # manually done for better granularity / ease of parsing strength data - def __generate_wireles_message(self, ssid, strength): - computed_strength = 100 * ((strength + 100) / 70.0) - return " {} {}%".format(ssid, int(computed_strength)) + def __generate_wireles_message(self, ssid, signal): + computed_strength = self.__compute_strength(signal) + return "{} {}%".format(ssid, int(computed_strength)) + + def __compute_strength(self, signal): + return 100 * ((signal + 100) / 70.0) + + # get signal strength in decibels/milliwat + def __compute_signal(self, interface): + # Get connection strength + cmd = "iwconfig {}".format(interface) + config_dat = " ".join(util.cli.execute(cmd).split()) + config_tokens = config_dat.replace("=", " ").split() + signal = config_tokens[config_tokens.index("level") + 1] + + return signal From 2100a7cfdbb5b768136c5c5c01a087f8541229db Mon Sep 17 00:00:00 2001 From: nepoz Date: Thu, 8 Jul 2021 12:10:46 -0500 Subject: [PATCH 030/301] Set up initial testing framework for network module --- bumblebee_status/modules/contrib/network.py | 2 +- tests/modules/contrib/test_network.py | 65 +++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 tests/modules/contrib/test_network.py diff --git a/bumblebee_status/modules/contrib/network.py b/bumblebee_status/modules/contrib/network.py index d90a21b..26c7889 100644 --- a/bumblebee_status/modules/contrib/network.py +++ b/bumblebee_status/modules/contrib/network.py @@ -33,7 +33,7 @@ class Module(core.module.Module): try: socket.create_connection(("1.1.1.1", 53)) self.__is_connected = True - except OSError: + except: self.__is_connected = False # Attempt to extract a valid network interface device diff --git a/tests/modules/contrib/test_network.py b/tests/modules/contrib/test_network.py new file mode 100644 index 0000000..e171d69 --- /dev/null +++ b/tests/modules/contrib/test_network.py @@ -0,0 +1,65 @@ +import pytest +from unittest import TestCase, mock + +import core.config +import core.widget +import modules.contrib.network + +def build_module(): + config = core.config.Config([]) + return modules.contrib.network.Module(config=config, theme=None) + +def wireless_default(): + return { + "default": { + 1: ('10.0.1.12', 'wlan0') + } + } + +def wired_default(): + return { + 'default': { + 18: ('10.0.1.12', 'eth0') + } + } + +def exec_side_effect(*args, **kwargs): + if args[0] == "iwgetid": + return "ESSID: bumblefoo" + elif "iwconfig" in args[0]: + return "level=-30" + + return "default" + + +class TestNetworkUnit(TestCase): + def test_load_module(self): + __import__("modules.contrib.network") + + @pytest.mark.allow_hosts(['127.0.0.1']) + def test_no_internet(self): + module = build_module() + assert module.widgets()[0].full_text() == "No connection" + + @mock.patch('util.cli.execute') + @mock.patch('netifaces.gateways') + @mock.patch('netifaces.AF_INET', 1) + def test_wireless_connection(self, gateways_mock, execute_mock): + fake_ssid = "bumblefoo" + gateways_mock.return_value = wireless_default() + execute_mock.side_effect = exec_side_effect + + module = build_module() + + assert fake_ssid in module.widgets()[0].full_text() + + @mock.patch('util.cli.execute') + @mock.patch('netifaces.gateways') + @mock.patch('netifaces.AF_INET', 18) + def test_wired_connection(self, gateways_mock, execute_mock): + gateways_mock.return_value = wired_default() + execute_mock.side_effect = exec_side_effect + + module = build_module() + + assert module.widgets()[0].full_text() == "Ethernet" From f9017c3a38e94d8ab67cc15d71e12bf0a63ca3e0 Mon Sep 17 00:00:00 2001 From: nepoz Date: Thu, 8 Jul 2021 22:55:23 -0500 Subject: [PATCH 031/301] Added more tests and exception handling --- bumblebee_status/modules/contrib/network.py | 43 ++++++---- tests/modules/contrib/test_network.py | 92 +++++++++++++++------ 2 files changed, 96 insertions(+), 39 deletions(-) diff --git a/bumblebee_status/modules/contrib/network.py b/bumblebee_status/modules/contrib/network.py index 26c7889..6c35ee4 100644 --- a/bumblebee_status/modules/contrib/network.py +++ b/bumblebee_status/modules/contrib/network.py @@ -1,8 +1,6 @@ """ A module to show currently active network connection (ethernet or wifi) and connection strength if the connection is wireless. -Dependencies: nm-connection-editor if users would like a graphical -network manager when left-clicking the module """ @@ -18,7 +16,7 @@ import socket class Module(core.module.Module): - @core.decorators.every(seconds=10) + @core.decorators.every(seconds=5) def __init__(self, config, theme): super().__init__(config, theme, core.widget.Widget(self.network)) self.__is_wireless = False @@ -33,20 +31,26 @@ class Module(core.module.Module): try: socket.create_connection(("1.1.1.1", 53)) self.__is_connected = True - except: + except Exception: self.__is_connected = False # Attempt to extract a valid network interface device - self.__interface = netifaces.gateways()["default"][netifaces.AF_INET][1] + try: + self.__interface = netifaces.gateways()["default"][netifaces.AF_INET][1] + except Exception: + self.__interface = None # Check to see if the interface (if connected to the internet) is wireless - if self.__is_connected: - with open("/proc/net/wireless", "r") as f: - self.__is_wireless = self.__interface in f.read() - f.close() + if self.__is_connected and self.__interface: + try: + with open("/proc/net/wireless", "r") as f: + self.__is_wireless = self.__interface in f.read() + f.close() + except Exception: + self.__is_wireless = False # setup message to send to the user - if not self.__is_connected: + if not self.__is_connected or not self.__interface: self.__message = "No connection" elif not self.__is_wireless: # Assuming that if user is connected via non-wireless means that it will be ethernet @@ -57,7 +61,11 @@ class Module(core.module.Module): iw_dat = util.cli.execute("iwgetid") has_ssid = "ESSID" in iw_dat signal = self.__compute_signal(self.__interface) - self.__signal = util.format.asint(signal, minimum=-110, maximum=-30) + + # If signal is None, that means that we can't compute the default interface's signal strength + self.__signal = ( + util.format.asint(signal, minimum=-110, maximum=-30) if signal else None + ) ssid = ( iw_dat[iw_dat.index(":") + 1 :].replace('"', "").strip() @@ -80,10 +88,12 @@ class Module(core.module.Module): # manually done for better granularity / ease of parsing strength data def __generate_wireles_message(self, ssid, signal): computed_strength = self.__compute_strength(signal) - return "{} {}%".format(ssid, int(computed_strength)) + strength_str = str(computed_strength) if computed_strength else "?" + + return "{} {}%".format(ssid, strength_str) def __compute_strength(self, signal): - return 100 * ((signal + 100) / 70.0) + return int(100 * ((signal + 100) / 70.0)) if signal else None # get signal strength in decibels/milliwat def __compute_signal(self, interface): @@ -91,6 +101,11 @@ class Module(core.module.Module): cmd = "iwconfig {}".format(interface) config_dat = " ".join(util.cli.execute(cmd).split()) config_tokens = config_dat.replace("=", " ").split() - signal = config_tokens[config_tokens.index("level") + 1] + + # handle weird output + try: + signal = config_tokens[config_tokens.index("level") + 1] + except Exception: + signal = None return signal diff --git a/tests/modules/contrib/test_network.py b/tests/modules/contrib/test_network.py index e171d69..9b270bf 100644 --- a/tests/modules/contrib/test_network.py +++ b/tests/modules/contrib/test_network.py @@ -1,65 +1,107 @@ -import pytest from unittest import TestCase, mock +import pytest import core.config import core.widget import modules.contrib.network +import socket + +pytest.importorskip("netifaces") + + def build_module(): config = core.config.Config([]) return modules.contrib.network.Module(config=config, theme=None) + def wireless_default(): - return { - "default": { - 1: ('10.0.1.12', 'wlan0') - } - } + return {"default": {1: ("10.0.1.12", "wlan3")}} + def wired_default(): - return { - 'default': { - 18: ('10.0.1.12', 'eth0') - } - } + return {"default": {18: ("10.0.1.12", "eth3")}} -def exec_side_effect(*args, **kwargs): + +def exec_side_effect_valid(*args, **kwargs): if args[0] == "iwgetid": return "ESSID: bumblefoo" - elif "iwconfig" in args[0]: + if "iwconfig" in args[0]: return "level=-30" + return mock.DEFAULT - return "default" + +def exec_side_effect_invalid(*args, **kwargs): + return "invalid gibberish, can't parse for info" class TestNetworkUnit(TestCase): def test_load_module(self): __import__("modules.contrib.network") - @pytest.mark.allow_hosts(['127.0.0.1']) + @pytest.mark.allow_hosts(["127.0.0.1"]) def test_no_internet(self): module = build_module() assert module.widgets()[0].full_text() == "No connection" - @mock.patch('util.cli.execute') - @mock.patch('netifaces.gateways') - @mock.patch('netifaces.AF_INET', 1) - def test_wireless_connection(self, gateways_mock, execute_mock): + @mock.patch("util.cli.execute") + @mock.patch("netifaces.gateways") + @mock.patch("socket.create_connection") + @mock.patch("netifaces.AF_INET", 1) + @mock.patch("builtins.open", mock.mock_open(read_data="wlan3")) + def test_valid_wireless_connection(self, socket_mock, gateways_mock, execute_mock): + socket_mock.return_value = mock.MagicMock() fake_ssid = "bumblefoo" gateways_mock.return_value = wireless_default() - execute_mock.side_effect = exec_side_effect + execute_mock.side_effect = exec_side_effect_valid module = build_module() assert fake_ssid in module.widgets()[0].full_text() - @mock.patch('util.cli.execute') - @mock.patch('netifaces.gateways') - @mock.patch('netifaces.AF_INET', 18) - def test_wired_connection(self, gateways_mock, execute_mock): + @mock.patch("netifaces.gateways") + @mock.patch("socket.create_connection") + @mock.patch("netifaces.AF_INET", 18) + @mock.patch("builtins.open", mock.mock_open(read_data="wlan3")) + def test_valid_wired_connection(self, socket_mock, gateways_mock): gateways_mock.return_value = wired_default() - execute_mock.side_effect = exec_side_effect + socket_mock.return_value = mock.MagicMock() module = build_module() assert module.widgets()[0].full_text() == "Ethernet" + + @mock.patch("netifaces.gateways") + @mock.patch("socket.create_connection") + def test_invalid_gateways(self, socket_mock, gateways_mock): + socket_mock.return_value = mock.Mock() + gateways_mock.return_value = {"xyz": "abc"} + + module = build_module() + assert module.widgets()[0].full_text() == "No connection" + + @mock.patch("util.cli.execute") + @mock.patch("socket.create_connection") + @mock.patch("netifaces.gateways") + @mock.patch("netifaces.AF_INET", 1) + @mock.patch("builtins.open", mock.mock_open(read_data="wlan3")) + def test_invalid_execs(self, gateways_mock, socket_mock, execute_mock): + execute_mock.side_effect = exec_side_effect_invalid + socket_mock.return_value = mock.MagicMock() + gateways_mock.return_value = wireless_default() + + module = build_module() + + assert module.widgets()[0].full_text() == "Unknown ?%" + + @mock.patch("builtins.open", **{"return_value.raiseError.side_effect": Exception()}) + @mock.patch("socket.create_connection") + @mock.patch("netifaces.gateways") + @mock.patch("netifaces.AF_INET", 18) + @mock.patch("builtins.open", mock.mock_open(read_data="wlan3")) + def test_no_wireless_file(self, gateways_mock, socket_mock, mock_open): + gateways_mock.return_value = wired_default() + socket_mock.return_value = mock.MagicMock() + module = build_module() + + assert module.widgets()[0].full_text() == "Ethernet" From 48501fa53478075ed0d6ce5d66ddb3499264bb86 Mon Sep 17 00:00:00 2001 From: nepoz Date: Thu, 8 Jul 2021 23:00:57 -0500 Subject: [PATCH 032/301] Updated docstring --- bumblebee_status/modules/contrib/network.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bumblebee_status/modules/contrib/network.py b/bumblebee_status/modules/contrib/network.py index 6c35ee4..b8d16a4 100644 --- a/bumblebee_status/modules/contrib/network.py +++ b/bumblebee_status/modules/contrib/network.py @@ -1,5 +1,9 @@ """ -A module to show currently active network connection (ethernet or wifi) and connection strength if the connection is wireless. +A module to show the currently active network connection (ethernet or wifi) and connection strength if the connection is wireless. + +Requires the Python netifaces package and iw installed on Linux. + +A simpler take on nic and network_traffic. No extra config necessary! """ From 5d80a5a1a0470b72a79608d1952eb126f7d37ab4 Mon Sep 17 00:00:00 2001 From: nepoz Date: Fri, 9 Jul 2021 00:28:00 -0500 Subject: [PATCH 033/301] Slight refactoring to try and break apart networkmethod --- bumblebee_status/modules/contrib/network.py | 35 ++++++++++++++------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/bumblebee_status/modules/contrib/network.py b/bumblebee_status/modules/contrib/network.py index b8d16a4..a91c947 100644 --- a/bumblebee_status/modules/contrib/network.py +++ b/bumblebee_status/modules/contrib/network.py @@ -32,11 +32,7 @@ class Module(core.module.Module): # Get network information to display to the user def network(self, widgets): # Determine whether there is an internet connection - try: - socket.create_connection(("1.1.1.1", 53)) - self.__is_connected = True - except Exception: - self.__is_connected = False + self.__is_connected = self.__attempt_connection() # Attempt to extract a valid network interface device try: @@ -46,12 +42,7 @@ class Module(core.module.Module): # Check to see if the interface (if connected to the internet) is wireless if self.__is_connected and self.__interface: - try: - with open("/proc/net/wireless", "r") as f: - self.__is_wireless = self.__interface in f.read() - f.close() - except Exception: - self.__is_wireless = False + self.__is_wireless = self.__interface_is_wireless(self.__interface) # setup message to send to the user if not self.__is_connected or not self.__interface: @@ -113,3 +104,25 @@ class Module(core.module.Module): signal = None return signal + + def __attempt_connection(self): + can_connect = False + try: + socket.create_connection(("1.1.1.1", 53)) + can_connect = True + except Exception: + can_connect = False + + return can_connect + + def __interface_is_wireless(self, interface): + is_wireless = False + try: + with open("/proc/net/wireless", "r") as f: + is_wireless = interface in f.read() + f.close() + except Exception: + is_wireless = False + + return is_wireless + From 5a2dfc226b5350f70adc790f9650217351540121 Mon Sep 17 00:00:00 2001 From: nepoz Date: Fri, 9 Jul 2021 00:58:09 -0500 Subject: [PATCH 034/301] Removed dependency on pytest-socket for the Network module's unit tests. --- tests/modules/contrib/test_network.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/modules/contrib/test_network.py b/tests/modules/contrib/test_network.py index 9b270bf..05ec3b1 100644 --- a/tests/modules/contrib/test_network.py +++ b/tests/modules/contrib/test_network.py @@ -39,8 +39,9 @@ class TestNetworkUnit(TestCase): def test_load_module(self): __import__("modules.contrib.network") - @pytest.mark.allow_hosts(["127.0.0.1"]) - def test_no_internet(self): + @mock.patch("socket.create_connection") + def test_no_internet(self, socket_mock): + socket_mock.side_effect = Exception() module = build_module() assert module.widgets()[0].full_text() == "No connection" From 98c92bb78facdf0d8456127a437d7fa4b2014839 Mon Sep 17 00:00:00 2001 From: Tom Saleeba Date: Sat, 24 Jul 2021 15:18:04 -0600 Subject: [PATCH 035/301] feat: add GPU usage % and GPU memory usage % to nvidiagpu --- bumblebee_status/modules/contrib/nvidiagpu.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/bumblebee_status/modules/contrib/nvidiagpu.py b/bumblebee_status/modules/contrib/nvidiagpu.py index 4aa9de9..2731314 100644 --- a/bumblebee_status/modules/contrib/nvidiagpu.py +++ b/bumblebee_status/modules/contrib/nvidiagpu.py @@ -4,7 +4,7 @@ Parameters: * nvidiagpu.format: Format string (defaults to '{name}: {temp}°C %{usedmem}/{totalmem} MiB') - Available values are: {name} {temp} {mem_used} {mem_total} {fanspeed} {clock_gpu} {clock_mem} + Available values are: {name} {temp} {mem_used} {mem_total} {fanspeed} {clock_gpu} {clock_mem} {gpu_usage_pct} {mem_usage_pct} Requires nvidia-smi @@ -41,6 +41,8 @@ class Module(core.module.Module): clockMem = "" clockGpu = "" fanspeed = "" + gpuUsagePct = "" + memPct = "" for item in sp.split("\n"): try: key, val = item.split(":") @@ -61,6 +63,11 @@ class Module(core.module.Module): name = val elif key == "Fan Speed": fanspeed = val.split(" ")[0] + elif title == "Utilization": + if key == "Gpu": + gpuUsagePct = val.split(" ")[0] + elif key == "Memory": + memPct = val.split(" ")[0] except: title = item.strip() @@ -76,6 +83,8 @@ class Module(core.module.Module): clock_gpu=clockGpu, clock_mem=clockMem, fanspeed=fanspeed, + gpu_usage_pct=gpuUsagePct, + mem_usage_pct=memPct, ) From f98053371e6fd297346c6c74522a1eb913fd2732 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Soykan=20Ert=C3=BCrk?= Date: Tue, 10 Aug 2021 20:19:30 +0300 Subject: [PATCH 036/301] Dependency for powerline themes --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 848434e..4727ebc 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,8 @@ There is also a SlackBuild available here: [slackbuilds:bumblebee-status](http:/ [Available modules](https://bumblebee-status.readthedocs.io/en/main/modules.html) lists the dependencies (Python modules and external executables) for each module. If you are not using a module, you don't need the dependencies. +Some themes (e.g. all ‘powerline’ themes) require Font Awesome http://fontawesome.io/ and a powerline-compatible font (powerline-fonts) https://github.com/powerline/fonts + # Usage ## Normal usage In your i3wm configuration, modify the *status_command* for your i3bar like this: From 473d2fbd1403bb475587a058ac9827202c7f4d8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Soykan=20Ert=C3=BCrk?= Date: Thu, 12 Aug 2021 22:54:34 +0300 Subject: [PATCH 037/301] Improving docs I added tkinter as dependency in requirements. --- bumblebee_status/modules/contrib/system.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bumblebee_status/modules/contrib/system.py b/bumblebee_status/modules/contrib/system.py index e96ee3f..cc46ef8 100644 --- a/bumblebee_status/modules/contrib/system.py +++ b/bumblebee_status/modules/contrib/system.py @@ -21,6 +21,9 @@ Parameters: * system.suspend: specify a command for suspending (defaults to 'i3exit suspend') * system.hibernate: specify a command for hibernating (defaults to 'i3exit hibernate') +Requirements: + tkinter (python3-tk package on debian based systems either you can install it as python package) + contributed by `bbernhard `_ - many thanks! """ From 8be9f1a05ca411a14e192df9e08313ea9eccaa5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Soykan=20Ert=C3=BCrk?= Date: Thu, 12 Aug 2021 23:15:09 +0300 Subject: [PATCH 038/301] system module requirement added tkinter as requirement --- docs/modules.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/modules.rst b/docs/modules.rst index 49e509b..5b9b075 100644 --- a/docs/modules.rst +++ b/docs/modules.rst @@ -1380,6 +1380,9 @@ Parameters: * system.lock: specify a command for locking the screen (defaults to 'i3exit lock') * system.suspend: specify a command for suspending (defaults to 'i3exit suspend') * system.hibernate: specify a command for hibernating (defaults to 'i3exit hibernate') + +Requirements: + tkinter (python3-tk package on debian based systems either you can install it as python package) contributed by `bbernhard `_ - many thanks! From 5a1addec7f26e0eac294842a91cc4125f9520e97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Soykan=20Ert=C3=BCrk?= Date: Sat, 14 Aug 2021 20:14:54 +0300 Subject: [PATCH 039/301] Fixing a small bug on todo module todo counts new lines (blank lines) as todo and increments todo count. After my fix todo doesn't counts blank lines. --- bumblebee_status/modules/contrib/todo.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/bumblebee_status/modules/contrib/todo.py b/bumblebee_status/modules/contrib/todo.py index df2d788..6c69f10 100644 --- a/bumblebee_status/modules/contrib/todo.py +++ b/bumblebee_status/modules/contrib/todo.py @@ -25,7 +25,7 @@ class Module(core.module.Module): self.__todos = self.count_items() core.input.register( self, button=core.input.LEFT_MOUSE, cmd="{} {}".format(self.__editor, self.__doc) - ) + ) def output(self, widget): return str(self.__todos) @@ -40,11 +40,12 @@ class Module(core.module.Module): def count_items(self): try: - i = -1 + i = 0 with open(self.__doc) as f: - for i, l in enumerate(f): - pass - return i + 1 + for l in f.readlines(): + if l.strip() != '': + i += 1 + return i except Exception: return 0 From 05f76c0d9a236426f6e99a48c6b685f375395280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Soykan=20Ert=C3=BCrk?= Date: Sat, 14 Aug 2021 20:16:45 +0300 Subject: [PATCH 040/301] Update todo.py --- bumblebee_status/modules/contrib/todo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bumblebee_status/modules/contrib/todo.py b/bumblebee_status/modules/contrib/todo.py index 6c69f10..76e289e 100644 --- a/bumblebee_status/modules/contrib/todo.py +++ b/bumblebee_status/modules/contrib/todo.py @@ -25,7 +25,7 @@ class Module(core.module.Module): self.__todos = self.count_items() core.input.register( self, button=core.input.LEFT_MOUSE, cmd="{} {}".format(self.__editor, self.__doc) - ) + ) def output(self, widget): return str(self.__todos) From d4339f6e43359cf536a897f33353840a39e9312b Mon Sep 17 00:00:00 2001 From: Tom Saleeba Date: Sun, 15 Aug 2021 22:27:44 -0600 Subject: [PATCH 041/301] fix: correct mem usage to be mem *io* usage --- bumblebee_status/modules/contrib/nvidiagpu.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/bumblebee_status/modules/contrib/nvidiagpu.py b/bumblebee_status/modules/contrib/nvidiagpu.py index 2731314..3b929a2 100644 --- a/bumblebee_status/modules/contrib/nvidiagpu.py +++ b/bumblebee_status/modules/contrib/nvidiagpu.py @@ -4,11 +4,15 @@ Parameters: * nvidiagpu.format: Format string (defaults to '{name}: {temp}°C %{usedmem}/{totalmem} MiB') - Available values are: {name} {temp} {mem_used} {mem_total} {fanspeed} {clock_gpu} {clock_mem} {gpu_usage_pct} {mem_usage_pct} + Available values are: {name} {temp} {mem_used} {mem_total} {fanspeed} {clock_gpu} {clock_mem} {gpu_usage_pct} {mem_usage_pct} {mem_io_pct} Requires nvidia-smi contributed by `RileyRedpath `_ - many thanks! + +Note: mem_io_pct is (from `man nvidia-smi`): +> Percent of time over the past sample period during which global (device) +> memory was being read or written. """ import core.module @@ -42,7 +46,8 @@ class Module(core.module.Module): clockGpu = "" fanspeed = "" gpuUsagePct = "" - memPct = "" + memIoPct = "" + memUsage = "not found" for item in sp.split("\n"): try: key, val = item.split(":") @@ -67,11 +72,14 @@ class Module(core.module.Module): if key == "Gpu": gpuUsagePct = val.split(" ")[0] elif key == "Memory": - memPct = val.split(" ")[0] + memIoPct = val.split(" ")[0] except: title = item.strip() + if totalMem and usedMem: + memUsage = int(int(usedMem) / int(totalMem) * 100) + str_format = self.parameter( "format", "{name}: {temp}°C {mem_used}/{mem_total} MiB" ) @@ -84,7 +92,8 @@ class Module(core.module.Module): clock_mem=clockMem, fanspeed=fanspeed, gpu_usage_pct=gpuUsagePct, - mem_usage_pct=memPct, + mem_io_pct=memIoPct, + mem_usage_pct=memUsage, ) From ed5a4e61e482d8e20ea0319cdb6c58f3d78083eb Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Fri, 10 Sep 2021 12:45:11 +0200 Subject: [PATCH 042/301] [modules/bluetooth] Add more error checking Do not kill the bar when the dbus-send command fails. see #818 --- bumblebee_status/modules/contrib/bluetooth.py | 2 +- bumblebee_status/modules/contrib/bluetooth2.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bumblebee_status/modules/contrib/bluetooth.py b/bumblebee_status/modules/contrib/bluetooth.py index b565494..481ae88 100644 --- a/bumblebee_status/modules/contrib/bluetooth.py +++ b/bumblebee_status/modules/contrib/bluetooth.py @@ -106,7 +106,7 @@ class Module(core.module.Module): ) logging.debug("bt: toggling bluetooth") - util.cli.execute(cmd) + util.cli.execute(cmd, ignore_errors=True) def state(self, widget): """Get current state.""" diff --git a/bumblebee_status/modules/contrib/bluetooth2.py b/bumblebee_status/modules/contrib/bluetooth2.py index 52474b9..22eae88 100644 --- a/bumblebee_status/modules/contrib/bluetooth2.py +++ b/bumblebee_status/modules/contrib/bluetooth2.py @@ -69,7 +69,7 @@ class Module(core.module.Module): ) logging.debug("bt: toggling bluetooth") - util.cli.execute(cmd) + util.cli.execute(cmd, ignore_errors=True) def state(self, widget): """Get current state.""" From c96d119b0e3b2550c709bc789f495c660599403c Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Thu, 7 Oct 2021 15:39:29 +0200 Subject: [PATCH 043/301] [core/config] add autohide to configuration file make it possible to configure a list of automatically hidden modules in the configuration file (+ add documentation for it). fixes #822 --- bumblebee_status/core/config.py | 2 +- docs/features.rst | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/bumblebee_status/core/config.py b/bumblebee_status/core/config.py index b5fd5ef..69a4ac6 100644 --- a/bumblebee_status/core/config.py +++ b/bumblebee_status/core/config.py @@ -328,7 +328,7 @@ class Config(util.store.Store): """ def autohide(self, name): - return name in self.__args.autohide + return name in self.__args.autohide or self.get("autohide") """Returns which modules should be hidden if they are in state error diff --git a/docs/features.rst b/docs/features.rst index f167033..3c7fe6e 100644 --- a/docs/features.rst +++ b/docs/features.rst @@ -160,6 +160,7 @@ Configuration files have the following format: [core] modules = + autohid = theme = [module-parameters] From b5395fe764c8b580475459e073a480f27525bb19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thayn=C3=A3=20Moretti?= Date: Mon, 11 Oct 2021 19:07:28 -0300 Subject: [PATCH 044/301] chore: public toggle method --- bumblebee_status/modules/contrib/dunstctl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bumblebee_status/modules/contrib/dunstctl.py b/bumblebee_status/modules/contrib/dunstctl.py index 3c803a4..f082f1b 100644 --- a/bumblebee_status/modules/contrib/dunstctl.py +++ b/bumblebee_status/modules/contrib/dunstctl.py @@ -24,12 +24,12 @@ import util.cli class Module(core.module.Module): def __init__(self, config, theme): super().__init__(config, theme, core.widget.Widget("")) - core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.__toggle_state) + core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.toggle_state) self.__states = {"unknown": ["unknown", "critical"], "true": ["muted", "warning"], "false": ["unmuted"]} - def __toggle_state(self, event): + def toggle_state(self, event): util.cli.execute("dunstctl set-paused toggle", ignore_errors=True) def state(self, widget): From 40de07ba2ed659abaa417ab7d76dff54c423b6e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thayn=C3=A3=20Moretti?= Date: Mon, 11 Oct 2021 19:08:46 -0300 Subject: [PATCH 045/301] chore: create dunst tests --- tests/modules/contrib/test_dunst.py | 53 +++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/tests/modules/contrib/test_dunst.py b/tests/modules/contrib/test_dunst.py index 2ca2d40..75e3151 100644 --- a/tests/modules/contrib/test_dunst.py +++ b/tests/modules/contrib/test_dunst.py @@ -1,5 +1,58 @@ import pytest +import core.config +import modules.contrib.dunst + + +def build_module(): + return modules.contrib.dunst.Module( + config=core.config.Config([]), + theme=None + ) + + def test_load_module(): __import__("modules.contrib.dunst") +def test_input_registration(mocker): + input_register = mocker.patch('core.input.register') + + module = build_module() + + input_register.assert_called_with( + module, + button=core.input.LEFT_MOUSE, + cmd=module.toggle_status + ) + +def test_dunst_toggle(mocker): + start_command = mocker.patch('util.cli.execute') + + module = build_module() + start_command.assert_called_with('killall -s SIGUSR2 dunst', ignore_errors=True) + + toggle_command = mocker.patch('util.cli.execute') + module.toggle_status(None) + toggle_command.assert_called_with('killall -s SIGUSR1 dunst') + + widget = module.widget() + actual = module.state(widget) + assert actual == ['muted', 'warning'] + + module.toggle_status(None) + toggle_command.assert_called_with('killall -s SIGUSR2 dunst') + + widget = module.widget() + actual = module.state(widget) + assert actual == ['unmuted'] + +def test_dunst_toggle_exception(mocker): + module = build_module() + + toggle_command = mocker.patch('util.cli.execute', side_effect=Exception) + module.toggle_status(None) + toggle_command.assert_called_with('killall -s SIGUSR1 dunst') + + widget = module.widget() + actual = module.state(widget) + assert actual == ['unmuted'] From 15809514740dcf0848b15e0328ff12f7d02420d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thayn=C3=A3=20Moretti?= Date: Mon, 11 Oct 2021 19:09:15 -0300 Subject: [PATCH 046/301] chore: create missing dunstctl tests --- tests/modules/contrib/test_dunstctl.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/modules/contrib/test_dunstctl.py b/tests/modules/contrib/test_dunstctl.py index db77fe3..2391f7e 100644 --- a/tests/modules/contrib/test_dunstctl.py +++ b/tests/modules/contrib/test_dunstctl.py @@ -1,6 +1,5 @@ import pytest -import util.cli import core.config import modules.contrib.dunstctl @@ -13,6 +12,25 @@ def build_module(): def test_load_module(): __import__("modules.contrib.dunstctl") +def test_input_registration(mocker): + input_register = mocker.patch('core.input.register') + + module = build_module() + + input_register.assert_called_with( + module, + button=core.input.LEFT_MOUSE, + cmd=module.toggle_state + ) + +def test_dunst_toggle_state(mocker): + command = mocker.patch('util.cli.execute') + + module = build_module() + + module.toggle_state(None) + command.assert_called_with('dunstctl set-paused toggle', ignore_errors=True) + def test_dunst_running(mocker): command = mocker.patch('util.cli.execute', return_value=(0, "false")) From 4007517e45a589e10a6111d5066f0a96694e2d6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thayn=C3=A3=20Moretti?= Date: Tue, 12 Oct 2021 11:24:43 -0300 Subject: [PATCH 047/301] chore: create libvirtvms tests --- tests/modules/contrib/test_libvirtvms.py | 43 +++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/tests/modules/contrib/test_libvirtvms.py b/tests/modules/contrib/test_libvirtvms.py index efa5880..48ba72c 100644 --- a/tests/modules/contrib/test_libvirtvms.py +++ b/tests/modules/contrib/test_libvirtvms.py @@ -1,7 +1,48 @@ +import sys import pytest +from unittest.mock import Mock -pytest.importorskip("libvirt") +import core.config + +sys.modules['libvirt'] = Mock() + +import modules.contrib.libvirtvms + +def build_module(): + return modules.contrib.libvirtvms.Module( + config=core.config.Config([]), + theme=None + ) def test_load_module(): __import__("modules.contrib.libvirtvms") +def test_input_registration(mocker): + input_register = mocker.patch('core.input.register') + + module = build_module() + + input_register.assert_called_with( + module, + button=core.input.LEFT_MOUSE, + cmd="virt-manager" + ) + +def test_status_failed(mocker): + mocker.patch('libvirt.openReadOnly', return_value=None) + + module = build_module() + status = module.status(None) + + assert status == "Failed to open connection to the hypervisor" + +def test_status(mocker): + virtMock = mocker.Mock() + virtMock.numOfDomains = mocker.Mock(return_value=10) + + mocker.patch('libvirt.openReadOnly', return_value=virtMock) + + module = build_module() + status = module.status(None) + + assert status == "VMs 10" From 0dc6a95ac284f105b534a546b5e244b161c84694 Mon Sep 17 00:00:00 2001 From: alexcoder04 Date: Thu, 21 Oct 2021 13:45:40 +0200 Subject: [PATCH 048/301] [modules/sensors] auto-determine the correct thermal zone --- bumblebee_status/modules/contrib/sensors.py | 69 +++++++++++++-------- docs/modules.rst | 2 + 2 files changed, 44 insertions(+), 27 deletions(-) diff --git a/bumblebee_status/modules/contrib/sensors.py b/bumblebee_status/modules/contrib/sensors.py index 8164f00..d023d6a 100644 --- a/bumblebee_status/modules/contrib/sensors.py +++ b/bumblebee_status/modules/contrib/sensors.py @@ -18,6 +18,7 @@ contributed by `mijoharas `_ - many thanks! """ import re +import os import json import logging @@ -49,19 +50,25 @@ class Module(core.module.Module): self.determine_method() def determine_method(self): + if self.parameter("use_sensors") == "True": + self.use_sensors = True + return + if self.parameter("use_sensors") == "False": + self.use_sensors = False + return if self.parameter("path") != None and self._json == False: - self.use_sensors = False # use thermal zone - else: - # try to use output of sensors -u - try: - output = util.cli.execute("sensors -u") - self.use_sensors = True - log.debug("Sensors command available") - except FileNotFoundError as e: - log.info( - "Sensors command not available, using /sys/class/thermal/thermal_zone*/" - ) - self.use_sensors = False + self.use_sensors = False # use thermal zone + return + # try to use output of sensors -u + try: + output = util.cli.execute("sensors -u") + self.use_sensors = True + log.debug("Sensors command available") + except FileNotFoundError as e: + log.info( + "Sensors command not available, using /sys/class/thermal/thermal_zone*/" + ) + self.use_sensors = False def _get_temp_from_sensors(self): if self._json == True: @@ -92,22 +99,30 @@ class Module(core.module.Module): def get_temp(self): if self.use_sensors: - temperature = self._get_temp_from_sensors() log.debug("Retrieve temperature from sensors -u") - else: - try: - temperature = open( - self.parameter("path", "/sys/class/thermal/thermal_zone0/temp") - ).read().strip() - log.debug("retrieved temperature from /sys/class/") - # TODO: Iterate through all thermal zones to determine the correct one and use its value - # https://unix.stackexchange.com/questions/304845/discrepancy-between-number-of-cores-and-thermal-zones-in-sys-class-thermal - - except IOError: - temperature = "unknown" - log.info("Can not determine temperature, please install lm-sensors") - - return temperature + return self._get_temp_from_sensors() + try: + path = None + # use path provided by the user + if self.parameter("path") is not None: + path = self.parameter("path") + # find the thermal zone that provides cpu temperature + for zone in os.listdir("/sys/class/thermal"): + if not zone.startswith("thermal_zone"): + continue + if open(f"/sys/class/thermal/{zone}/type").read().strip() != "x86_pkg_temp": + continue + path = f"/sys/class/thermal/{zone}/temp" + # use zone 0 as fallback + if path is None: + log.info("Can not determine temperature path, using thermal_zone0") + path = "/sys/class/thermal/thermal_zone0/temp" + log.debug(f"retrieving temperature from {path}") + # the values are t°C * 1000, so divide by 1000 + return str(int(open(path).read()) / 1000) + except IOError: + log.info("Can not determine temperature, please install lm-sensors") + return "unknown" def get_mhz(self): mhz = None diff --git a/docs/modules.rst b/docs/modules.rst index 5b9b075..ded47dc 100644 --- a/docs/modules.rst +++ b/docs/modules.rst @@ -1195,6 +1195,8 @@ sensors Displays sensor temperature Parameters: + * sensors.use_sensors (True/False): whether to use the 'sensors' command. + If set to 'False', the sysfs-interface at '/sys/class/thermal' is used * sensors.path: path to temperature file (default /sys/class/thermal/thermal_zone0/temp). * sensors.json: if set to 'true', interpret sensors.path as JSON 'path' in the output of 'sensors -j' (i.e. //.../), for example, path could From 6b31cdb698e4086dd07c769d11ebbec8f6c84425 Mon Sep 17 00:00:00 2001 From: alexcoder04 Date: Thu, 21 Oct 2021 14:43:15 +0200 Subject: [PATCH 049/301] [modules/sensors] use util.format.asbool() + auto-check only if no path is specified --- bumblebee_status/modules/contrib/sensors.py | 35 ++++++++++----------- docs/modules.rst | 5 +-- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/bumblebee_status/modules/contrib/sensors.py b/bumblebee_status/modules/contrib/sensors.py index d023d6a..7d42c83 100644 --- a/bumblebee_status/modules/contrib/sensors.py +++ b/bumblebee_status/modules/contrib/sensors.py @@ -4,6 +4,7 @@ """Displays sensor temperature Parameters: + * sensors.use_sensors: whether to use the sensors command * sensors.path: path to temperature file (default /sys/class/thermal/thermal_zone0/temp). * sensors.json: if set to 'true', interpret sensors.path as JSON 'path' in the output of 'sensors -j' (i.e. //.../), for example, path could @@ -47,28 +48,25 @@ class Module(core.module.Module): self._json = util.format.asbool(self.parameter("json", False)) self._freq = util.format.asbool(self.parameter("show_freq", True)) core.input.register(self, button=core.input.LEFT_MOUSE, cmd="xsensors") - self.determine_method() + self.use_sensors = self.determine_method() def determine_method(self): - if self.parameter("use_sensors") == "True": - self.use_sensors = True - return - if self.parameter("use_sensors") == "False": - self.use_sensors = False - return + if util.format.asbool(self.parameter("use_sensors")) == True: + return True + if util.format.asbool(self.parameter("use_sensors")) == False: + return False if self.parameter("path") != None and self._json == False: - self.use_sensors = False # use thermal zone - return + return False # try to use output of sensors -u try: - output = util.cli.execute("sensors -u") - self.use_sensors = True + _ = util.cli.execute("sensors -u") log.debug("Sensors command available") + return True except FileNotFoundError as e: log.info( "Sensors command not available, using /sys/class/thermal/thermal_zone*/" ) - self.use_sensors = False + return False def _get_temp_from_sensors(self): if self._json == True: @@ -107,12 +105,13 @@ class Module(core.module.Module): if self.parameter("path") is not None: path = self.parameter("path") # find the thermal zone that provides cpu temperature - for zone in os.listdir("/sys/class/thermal"): - if not zone.startswith("thermal_zone"): - continue - if open(f"/sys/class/thermal/{zone}/type").read().strip() != "x86_pkg_temp": - continue - path = f"/sys/class/thermal/{zone}/temp" + else: + for zone in os.listdir("/sys/class/thermal"): + if not zone.startswith("thermal_zone"): + continue + if open(f"/sys/class/thermal/{zone}/type").read().strip() != "x86_pkg_temp": + continue + path = f"/sys/class/thermal/{zone}/temp" # use zone 0 as fallback if path is None: log.info("Can not determine temperature path, using thermal_zone0") diff --git a/docs/modules.rst b/docs/modules.rst index ded47dc..442d461 100644 --- a/docs/modules.rst +++ b/docs/modules.rst @@ -1195,8 +1195,9 @@ sensors Displays sensor temperature Parameters: - * sensors.use_sensors (True/False): whether to use the 'sensors' command. - If set to 'False', the sysfs-interface at '/sys/class/thermal' is used + * sensors.use_sensors: whether to use the 'sensors' command. + If set to 'false', the sysfs-interface at '/sys/class/thermal' is used. + If not set, 'sensors' will be used if available. * sensors.path: path to temperature file (default /sys/class/thermal/thermal_zone0/temp). * sensors.json: if set to 'true', interpret sensors.path as JSON 'path' in the output of 'sensors -j' (i.e. //.../), for example, path could From 876774ce406f2fe55a49667c73e2ad8a7628a0e2 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Sat, 23 Oct 2021 13:43:08 +0200 Subject: [PATCH 050/301] [travis] removing python 3.4 and 3.5 Since 3.4 and 3.5 are both not supported anymore, do not do unit tests for them. --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 369aeb7..c59ad53 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,6 @@ env: global: - CC_TEST_REPORTER_ID=40cb00907f7a10e04868e856570bb997ab9c42fd3b63d980f2b2269433195fdf python: - - "3.4" - - "3.5" - "3.6" - "3.7" - "3.8" From 99bb5e99aa052788f9a382a21d465ffe676445e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thayn=C3=A3=20Moretti?= Date: Sat, 23 Oct 2021 10:12:49 -0300 Subject: [PATCH 051/301] Fix Travis CI build URL --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4727ebc..4511a7d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # bumblebee-status -[![Build Status](https://travis-ci.com/tobi-wan-kenobi/bumblebee-status.svg?branch=main)](https://travis-ci.com/tobi-wan-kenobi/bumblebee-status) +[![Build Status](https://app.travis-ci.com/tobi-wan-kenobi/bumblebee-status.svg?branch=main)](https://app.travis-ci.com/tobi-wan-kenobi/bumblebee-status) [![Documentation Status](https://readthedocs.org/projects/bumblebee-status/badge/?version=main)](https://bumblebee-status.readthedocs.io/en/main/?badge=main) ![AUR version (release)](https://img.shields.io/aur/version/bumblebee-status) ![AUR version (git)](https://img.shields.io/aur/version/bumblebee-status-git) From 7542a47dbc97e92b1c19e12d6bf588ef59fd78b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thayn=C3=A3=20Moretti?= Date: Sat, 23 Oct 2021 11:02:46 -0300 Subject: [PATCH 052/301] fix: generate coverage xml before cc report --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index c59ad53..b05ac86 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,4 +26,5 @@ install: script: - coverage run --source=. -m pytest tests -v after_script: + - coverage xml - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT From d8216a5e2c05d9480faae0334216f22111bd3ae8 Mon Sep 17 00:00:00 2001 From: Kushagra Jain Date: Tue, 26 Oct 2021 13:54:35 +0530 Subject: [PATCH 053/301] added new theme --- themes/nord-colorful.json | 59 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 themes/nord-colorful.json diff --git a/themes/nord-colorful.json b/themes/nord-colorful.json new file mode 100644 index 0000000..acd334b --- /dev/null +++ b/themes/nord-colorful.json @@ -0,0 +1,59 @@ +{ + "icons": [ "awesome-fonts" ], + "colors": [{ + "red": "#BF616A", + "orange": "#D08770", + "yellow": "#EBCB8B", + "green": "#A3BE8C" + }], + "defaults": { + "separator-block-width": 0, + "warning": { + "fg": "#2E3440", + "bg": "#D08770" + }, + "critical": { + "fg": "#2E3440", + "bg": "#BF616A" + } + }, + "cycle": [ + { "fg": "#000000", "bg": "#8FBCBB"}, + { "fg": "#000000", "bg": "#A3BE8C"}, + { "fg": "#000000", "bg": "#EBCB8B"}, + { "fg": "#000000", "bg": "#BF616A"}, + { "fg": "#000000", "bg": "#B48EAD"} + ], + "dnf": { + "good": { + "fg": "#A3BE8C", + "bg": "#2E3440" + } + }, + "apt": { + "good": { + "fg": "#A3BE8C", + "bg": "#2E3440" + } + }, + "pacman": { + "good": { + "fg": "#A3BE8C", + "bg": "#2E3440" + } + }, + "pomodoro": { + "paused": { + "fg": "#2E3440", + "bg": "#D08770" + }, + "work": { + "fg": "#2E3440", + "bg": "#EBCB8B" + }, + "break": { + "fg": "#A3BE8C", + "bg": "#2E3440" + } + } +} From fdc9b7896715d3bfd918f9ec0a264fbd35c52af5 Mon Sep 17 00:00:00 2001 From: Jan Fader Date: Tue, 26 Oct 2021 19:27:02 +0200 Subject: [PATCH 054/301] add new solaar.py for logitech's unifying devices --- bumblebee_status/modules/contrib/solaar.py | 58 ++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 bumblebee_status/modules/contrib/solaar.py diff --git a/bumblebee_status/modules/contrib/solaar.py b/bumblebee_status/modules/contrib/solaar.py new file mode 100644 index 0000000..a718fb2 --- /dev/null +++ b/bumblebee_status/modules/contrib/solaar.py @@ -0,0 +1,58 @@ +"""Shows status and load percentage of logitech's unifying device + +Requires the following executable: + * solaar (from community) + +contributed by `cambid `_ - many thanks! +""" + +import logging + +import core.module +import core.widget +import core.decorators + +import util.cli + + +class Module(core.module.Module): + @core.decorators.every(seconds=30) + def __init__(self, config, theme): + super().__init__(config, theme, core.widget.Widget(self.utilization)) + self.__battery = self.parameter("device", "") + self.background = True + self.__battery_status = "" + self.__error = False + + @property + def __format(self): + return self.parameter("format", "{}") + + def utilization(self, widget): + return self.__format.format(self.__battery_status) + + def update(self): + self.__error = False + if self.__battery != "": + cmd = f"solaar show '{self.__battery}'" + else: + cmd = "solaar show" + code, result = util.cli.execute( + cmd, ignore_errors=True, return_exitcode=True + ) + + if code == 0: + for line in result.split('\n'): + if line.count('Battery') > 0: + self.__battery_status = line.split(':')[1].strip() + else: + self.__error = True + logging.error(f"solaar exited with {code}: {result}") + + def state(self, widget): + if self.__error: + return "warning" + return "okay" + + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From dced20bf892dbe68565ebc59ae0c41b2131de203 Mon Sep 17 00:00:00 2001 From: Jan Fader Date: Tue, 26 Oct 2021 19:52:42 +0200 Subject: [PATCH 055/301] refactor code to decrease cognitive complexity in update --- bumblebee_status/modules/contrib/solaar.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bumblebee_status/modules/contrib/solaar.py b/bumblebee_status/modules/contrib/solaar.py index a718fb2..b7396f3 100644 --- a/bumblebee_status/modules/contrib/solaar.py +++ b/bumblebee_status/modules/contrib/solaar.py @@ -23,6 +23,10 @@ class Module(core.module.Module): self.background = True self.__battery_status = "" self.__error = False + if self.__battery != "": + self.__cmd = f"solaar show '{self.__battery}'" + else: + self.__cmd = "solaar show" @property def __format(self): @@ -33,12 +37,8 @@ class Module(core.module.Module): def update(self): self.__error = False - if self.__battery != "": - cmd = f"solaar show '{self.__battery}'" - else: - cmd = "solaar show" code, result = util.cli.execute( - cmd, ignore_errors=True, return_exitcode=True + self.__cmd, ignore_errors=True, return_exitcode=True ) if code == 0: From 2cb72fcc3027fc74a4d9df2d76b1f0cf1a55be57 Mon Sep 17 00:00:00 2001 From: Jan Fader Date: Tue, 26 Oct 2021 21:52:15 +0200 Subject: [PATCH 056/301] add tests for solaar.py --- tests/modules/contrib/test_solaar.py | 32 ++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 tests/modules/contrib/test_solaar.py diff --git a/tests/modules/contrib/test_solaar.py b/tests/modules/contrib/test_solaar.py new file mode 100644 index 0000000..9c5fe33 --- /dev/null +++ b/tests/modules/contrib/test_solaar.py @@ -0,0 +1,32 @@ +import pytest + +import util.cli +import core.config +import modules.contrib.solaar + + +@pytest.fixture +def module(): + module = modules.contrib.solaar.Module( + config=core.config.Config([]), + theme=None + ) + + yield module + + +def test_load_module(): + __import__("modules.contrib.solaar") + + +def test_with_unknown_code(module, mocker): + mocker.patch('util.cli.execute', return_value=(99, 'error')) + logger = mocker.patch('logging.error') + + module.update() + + logger.assert_called_with('solaar exited with {}: {}'.format(99, 'error')) + + widget = module.widget() + assert module.state(widget) == 'warning' + assert module.hidden() == False From c7f58ae2a4412eb4cd8c017fca7b544ff87a3a11 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Fri, 5 Nov 2021 08:43:33 +0100 Subject: [PATCH 057/301] [doc] fix typo (autohid vs. autohide) see #835 --- docs/features.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features.rst b/docs/features.rst index 3c7fe6e..603c7fe 100644 --- a/docs/features.rst +++ b/docs/features.rst @@ -160,7 +160,7 @@ Configuration files have the following format: [core] modules = - autohid = + autohide = theme = [module-parameters] From f0ab3ef03a9f993adb0ff48479b405d9dcfe9832 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Fri, 5 Nov 2021 13:57:05 +0100 Subject: [PATCH 058/301] [core/config] fix autohide parameter from configuration file Need to parse the config parameter "autohide" into a list and actually check whether the current module is in the list. see #835 --- bumblebee_status/core/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bumblebee_status/core/config.py b/bumblebee_status/core/config.py index 69a4ac6..f191673 100644 --- a/bumblebee_status/core/config.py +++ b/bumblebee_status/core/config.py @@ -328,7 +328,7 @@ class Config(util.store.Store): """ def autohide(self, name): - return name in self.__args.autohide or self.get("autohide") + return name in self.__args.autohide or name in util.format.aslist(self.get("autohide", [])) """Returns which modules should be hidden if they are in state error From cbd989309d39db1c35c56b3c7884dc3c42a3f679 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Fri, 5 Nov 2021 14:00:34 +0100 Subject: [PATCH 059/301] [contrib/progress] allow hiding of inactive state Add a new "hide-able" state "mayhide" that can be utilized by modules without warning state. This state indicates that the module *may* be hidden by autohide, if the user configures it like this. see #835 --- bumblebee_status/core/output.py | 2 +- bumblebee_status/modules/contrib/progress.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bumblebee_status/core/output.py b/bumblebee_status/core/output.py index 121df27..cf79f2f 100644 --- a/bumblebee_status/core/output.py +++ b/bumblebee_status/core/output.py @@ -229,7 +229,7 @@ class i3(object): for widget in module.widgets(): if widget.module and self.__config.autohide(widget.module.name): if not any( - state in widget.state() for state in ["warning", "critical"] + state in widget.state() for state in ["warning", "critical", "mayhide"] ): continue if module.hidden(): diff --git a/bumblebee_status/modules/contrib/progress.py b/bumblebee_status/modules/contrib/progress.py index a1938d2..e818197 100644 --- a/bumblebee_status/modules/contrib/progress.py +++ b/bumblebee_status/modules/contrib/progress.py @@ -102,7 +102,7 @@ class Module(core.module.Module): def state(self, widget): if self.__active: return "copying" - return "pending" + return ["pending", "mayhide"] # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From 74ecbb6ca8e3202cafc69bfbf332081a5eec85cb Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Fri, 5 Nov 2021 19:11:45 +0100 Subject: [PATCH 060/301] [core/output] fix logic error when using "autohide" - when state is critical or warning -> *show* the module - when state is mayhide -> *hide* the module see #835 --- bumblebee_status/core/output.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bumblebee_status/core/output.py b/bumblebee_status/core/output.py index cf79f2f..47283eb 100644 --- a/bumblebee_status/core/output.py +++ b/bumblebee_status/core/output.py @@ -229,9 +229,11 @@ class i3(object): for widget in module.widgets(): if widget.module and self.__config.autohide(widget.module.name): if not any( - state in widget.state() for state in ["warning", "critical", "mayhide"] + state in widget.state() for state in ["warning", "critical"] ): continue + if "mayhide" in widget.state(): + continue if module.hidden(): continue if widget.hidden: From 6a3e4761bf7d09e786dd3e0cf43e1f826db47278 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Sat, 6 Nov 2021 08:17:11 +0100 Subject: [PATCH 061/301] Revert "[core/output] fix logic error when using "autohide"" This reverts commit 74ecbb6ca8e3202cafc69bfbf332081a5eec85cb. --- bumblebee_status/core/output.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bumblebee_status/core/output.py b/bumblebee_status/core/output.py index 47283eb..cf79f2f 100644 --- a/bumblebee_status/core/output.py +++ b/bumblebee_status/core/output.py @@ -229,11 +229,9 @@ class i3(object): for widget in module.widgets(): if widget.module and self.__config.autohide(widget.module.name): if not any( - state in widget.state() for state in ["warning", "critical"] + state in widget.state() for state in ["warning", "critical", "mayhide"] ): continue - if "mayhide" in widget.state(): - continue if module.hidden(): continue if widget.hidden: From 5ad211f86206181ee8b6284d195185065b55916b Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Sat, 6 Nov 2021 08:17:18 +0100 Subject: [PATCH 062/301] Revert "[contrib/progress] allow hiding of inactive state" This reverts commit cbd989309d39db1c35c56b3c7884dc3c42a3f679. --- bumblebee_status/core/output.py | 2 +- bumblebee_status/modules/contrib/progress.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bumblebee_status/core/output.py b/bumblebee_status/core/output.py index cf79f2f..121df27 100644 --- a/bumblebee_status/core/output.py +++ b/bumblebee_status/core/output.py @@ -229,7 +229,7 @@ class i3(object): for widget in module.widgets(): if widget.module and self.__config.autohide(widget.module.name): if not any( - state in widget.state() for state in ["warning", "critical", "mayhide"] + state in widget.state() for state in ["warning", "critical"] ): continue if module.hidden(): diff --git a/bumblebee_status/modules/contrib/progress.py b/bumblebee_status/modules/contrib/progress.py index e818197..a1938d2 100644 --- a/bumblebee_status/modules/contrib/progress.py +++ b/bumblebee_status/modules/contrib/progress.py @@ -102,7 +102,7 @@ class Module(core.module.Module): def state(self, widget): if self.__active: return "copying" - return ["pending", "mayhide"] + return "pending" # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From 26e4bdd7eb9fd8c8509693cd7d061e065c4f348f Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Sat, 6 Nov 2021 08:21:08 +0100 Subject: [PATCH 063/301] [modules/progress] improved autohide functionality Simplify the previous autohide functionality by adding a flag that lets a module (e.g. progress) indicate that the current state should be "revealed" (not auto-hidden). This vastly simplifies the implementation. see #835 --- bumblebee_status/core/output.py | 2 +- bumblebee_status/modules/contrib/progress.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bumblebee_status/core/output.py b/bumblebee_status/core/output.py index 121df27..cee579f 100644 --- a/bumblebee_status/core/output.py +++ b/bumblebee_status/core/output.py @@ -229,7 +229,7 @@ class i3(object): for widget in module.widgets(): if widget.module and self.__config.autohide(widget.module.name): if not any( - state in widget.state() for state in ["warning", "critical"] + state in widget.state() for state in ["warning", "critical", "no-autohide"] ): continue if module.hidden(): diff --git a/bumblebee_status/modules/contrib/progress.py b/bumblebee_status/modules/contrib/progress.py index a1938d2..7e148b3 100644 --- a/bumblebee_status/modules/contrib/progress.py +++ b/bumblebee_status/modules/contrib/progress.py @@ -101,7 +101,7 @@ class Module(core.module.Module): def state(self, widget): if self.__active: - return "copying" + return ["copying", "no-autohide"] return "pending" From a84b4f9a65cceb92f274c1a3ba275105abc820a2 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Sun, 7 Nov 2021 13:46:52 +0100 Subject: [PATCH 064/301] [docs] fix docs build pin docutils to < 0.18 as per https://github.com/readthedocs/readthedocs.org/issues/8616#issuecomment-952034858 --- .readthedocs.yaml | 6 ++++++ docs/requirements.txt | 1 + 2 files changed, 7 insertions(+) create mode 100644 .readthedocs.yaml create mode 100644 docs/requirements.txt diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..6823857 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,6 @@ +version: 2 + +python: + install: + - requirements: docs/requirements.txt + diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..93120e6 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1 @@ +docutils<0.18 From 6ce761695af9107c5ca3ca3e1679295545bc40ef Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Sun, 7 Nov 2021 13:50:44 +0100 Subject: [PATCH 065/301] [doc] remove "master" branch --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 4511a7d..2f7bd86 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,7 @@ Supported FontAwesome version: 4 (free version of 5 doesn't include some of the --- ***NOTE*** -The default branch for this project is `main` - I'm keeping `master` around for backwards compatibility (I do not want to break anybody's setup), but the default branch is now `main`! - -If you are curious why: [ZDNet:github-master-alternative](https://www.zdnet.com/article/github-to-replace-master-with-alternative-term-to-avoid-slavery-references/) +The default branch for this project is `main`. If you are curious why: [ZDNet:github-master-alternative](https://www.zdnet.com/article/github-to-replace-master-with-alternative-term-to-avoid-slavery-references/) --- From 973dd6117e273828c963697a9a56c95b132cb64f Mon Sep 17 00:00:00 2001 From: Yufan You Date: Fri, 17 Dec 2021 18:07:45 +0800 Subject: [PATCH 066/301] [contrib/playerctl]: don't log when no player is found `playerctl status` returns 1 when no player is found, which caused contrib/playerctl to log many times when there's no player. --- bumblebee_status/modules/contrib/playerctl.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bumblebee_status/modules/contrib/playerctl.py b/bumblebee_status/modules/contrib/playerctl.py index e02fa84..56af426 100755 --- a/bumblebee_status/modules/contrib/playerctl.py +++ b/bumblebee_status/modules/contrib/playerctl.py @@ -85,7 +85,9 @@ class Module(core.module.Module): def update(self): try: - playback_status = str(util.cli.execute(self.__cmd + "status")).strip() + playback_status = str(util.cli.execute(self.__cmd + "status 2>&1 || true", shell = True)).strip() + if playback_status == "No players found": + playback_status = None except Exception as e: logging.exception(e) playback_status = None From 8991bba90e6427e3fbc4da328303e7b09178362a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20L=C3=BCftinger?= Date: Tue, 28 Dec 2021 00:34:10 +0100 Subject: [PATCH 067/301] Silence exceptions in the spotify module which may write large amounts of logs to ~/.xsession-errors --- bumblebee_status/modules/contrib/spotify.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bumblebee_status/modules/contrib/spotify.py b/bumblebee_status/modules/contrib/spotify.py index 7544b48..1c7f7e2 100644 --- a/bumblebee_status/modules/contrib/spotify.py +++ b/bumblebee_status/modules/contrib/spotify.py @@ -159,7 +159,6 @@ class Module(core.module.Module): widget.full_text(self.__song) except Exception as e: - logging.exception(e) self.__song = "" @property From d430f904341360222f7ad51080aa7c3fe47044b2 Mon Sep 17 00:00:00 2001 From: Marco Genasci Date: Wed, 5 Jan 2022 09:34:27 +0100 Subject: [PATCH 068/301] Excluding the tests folder from the installation Signed-off-by: Marco Genasci --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 2e5bafb..1f6c203 100755 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """Setup file for bumbleestatus bar to allow pip install of full package""" # -*- coding: utf8 - *- -from setuptools import setup +from setuptools import setup, find_packages import versioneer with open("requirements/base.txt") as f: @@ -57,4 +57,5 @@ setup( ("share/bumblebee-status/themes/icons", glob.glob("themes/icons/*.json")), ("share/bumblebee-status/utility", glob.glob("bin/*")), ], + packages=find_packages(exclude=["tests", "tests.*"]) ) From 51c3805f7f8cc1c064c183e1c5b122cae2b568f7 Mon Sep 17 00:00:00 2001 From: Marco Genasci Date: Wed, 5 Jan 2022 09:34:38 +0100 Subject: [PATCH 069/301] Change deprecated dash-separated with underscore in setup.cfg Signed-off-by: Marco Genasci --- setup.cfg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 081fab6..d417aa6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -31,8 +31,8 @@ keywords = bumblebee-status [options] include_package_data = True -allow-all-external = yes -trusted-host = +allow_all_external = yes +trusted_host = gitlab.* bitbucket.org github.com From 8a50eb6f81ee31764950d70ecd47cb0ae17a784b Mon Sep 17 00:00:00 2001 From: Marco Genasci Date: Thu, 6 Jan 2022 08:06:57 +0100 Subject: [PATCH 070/301] New module emerge_status Display information about the currently running emerge process. Signed-off-by: Marco Genasci --- .../modules/contrib/emerge_status.py | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 bumblebee_status/modules/contrib/emerge_status.py diff --git a/bumblebee_status/modules/contrib/emerge_status.py b/bumblebee_status/modules/contrib/emerge_status.py new file mode 100644 index 0000000..3758585 --- /dev/null +++ b/bumblebee_status/modules/contrib/emerge_status.py @@ -0,0 +1,113 @@ +"""Display information about the currently running emerge process. + +Requires the following executable: + * emerge + +Parameters: + * emerge_status.format: Format string (defaults to '{current}/{total} {action} {category}/{pkg}') + +This code is based on emerge_status module from p3status [1] original created by AnwariasEu. + +[1] https://github.com/ultrabug/py3status/blob/master/py3status/modules/emerge_status.py +""" + +import re +import copy + +import core.module +import core.widget +import core.decorators + +import util.cli +import util.format + + +class Module(core.module.Module): + @core.decorators.every(seconds=10) + def __init__(self, config, theme): + super().__init__(config, theme, []) + self.__format = self.parameter( + "format", "{current}/{total} {action} {category}/{pkg}" + ) + self.__ret_default = { + "action": "", + "category": "", + "current": 0, + "pkg": "", + "total": 0, + } + + def update(self): + response = {} + ret = copy.deepcopy(self.__ret_default) + if self.__emerge_running(): + ret = self.__get_progress() + + widget = self.widget("status") + if not widget: + widget = self.add_widget(name="status") + + if ret["total"] == 0: + widget.full_text("emrg calculating...") + else: + widget.full_text( + " ".join( + self.__format.format( + current=ret["current"], + total=ret["total"], + action=ret["action"], + category=ret["category"], + pkg=ret["pkg"], + ).split() + ) + ) + else: + self.clear_widgets() + + def __emerge_running(self): + """ + Check if emerge is running. + Returns true if at least one instance of emerge is running. + """ + try: + util.cli.execute("pgrep emerge") + return True + except Exception: + return False + + def __get_progress(self): + """ + Get current progress of emerge. + Returns a dict containing current and total value. + """ + input_data = [] + ret = {} + + # traverse emerge.log from bottom up to get latest information + last_lines = util.cli.execute("tail -50 /var/log/emerge.log") + input_data = last_lines.split("\n") + input_data.reverse() + + for line in input_data: + if "*** terminating." in line: + # copy content of ret_default, not only the references + ret = copy.deepcopy(self.__ret_default) + break + else: + status_re = re.compile( + r"\((?P[\d]+) of (?P[\d]+)\) " + r"(?P[a-zA-Z/]+( [a-zA-Z]+)?) " + r"\((?P[\w\-]+)/(?P

[\w.]+)" + ) + res = status_re.search(line) + if res is not None: + ret["action"] = res.group("a").lower() + ret["category"] = res.group("ca") + ret["current"] = res.group("cu") + ret["pkg"] = res.group("p") + ret["total"] = res.group("t") + break + return ret + + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From f4ca5eaa3b177a874da07cc0946fd99b4016f239 Mon Sep 17 00:00:00 2001 From: Marco Genasci Date: Thu, 6 Jan 2022 08:07:49 +0100 Subject: [PATCH 071/301] Added documentation and screenshot for emerge_status module Signed-off-by: Marco Genasci --- docs/modules.rst | 15 +++++++++++++++ screenshots/emerge_status.png | Bin 0 -> 9403 bytes 2 files changed, 15 insertions(+) create mode 100644 screenshots/emerge_status.png diff --git a/docs/modules.rst b/docs/modules.rst index 442d461..413f6cf 100644 --- a/docs/modules.rst +++ b/docs/modules.rst @@ -725,6 +725,21 @@ contributed by `joachimmathes `_ - many thanks .. image:: ../screenshots/dunstctl.png +emerge_status +~~~~~~~~~~~~~ + +Display information about the currently running emerge process. + +Requires the following executable: + * emerge + +Parameters: + * emerge_status.format: Format string (defaults to '{current}/{total} {action} {category}/{pkg}') + +This code is based on `emerge_status module from p3status `_ original created by `AnwariasEu `_. + +.. image:: ../screenshots/emerge_status.png + getcrypto ~~~~~~~~~ diff --git a/screenshots/emerge_status.png b/screenshots/emerge_status.png new file mode 100644 index 0000000000000000000000000000000000000000..1e2a93aed29e07ccee8e56e98310c5b6d73b3217 GIT binary patch literal 9403 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 Date: Thu, 6 Jan 2022 10:13:40 +0100 Subject: [PATCH 072/301] Added ebuild link on README.md Signed-off-by: Marco Genasci --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 2f7bd86..59533bc 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,8 @@ pip install --user bumblebee-status There is also a SlackBuild available here: [slackbuilds:bumblebee-status](http://slackbuilds.org/repository/14.2/desktop/bumblebee-status/) - many thanks to [@Tonus1](https://github.com/Tonus1)! +An ebuild, for Gentoo Linux, is available on [gallifrey overlay](https://github.com/fedeliallalinea/gallifrey/tree/master/x11-misc/bumblebee-status). Instructions for adding the overlay can be found [here](https://github.com/fedeliallalinea/gallifrey/blob/master/README.md). + # Dependencies [Available modules](https://bumblebee-status.readthedocs.io/en/main/modules.html) lists the dependencies (Python modules and external executables) for each module. If you are not using a module, you don't need the dependencies. From 30dd0f2efb6778924f4d9d9fd34a61d32d2fc51c Mon Sep 17 00:00:00 2001 From: Frank Scherrer Date: Thu, 13 Jan 2022 11:05:01 +0100 Subject: [PATCH 073/301] add updating view on apt-cache on click --- bumblebee_status/modules/contrib/apt.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bumblebee_status/modules/contrib/apt.py b/bumblebee_status/modules/contrib/apt.py index b7200bb..575968f 100644 --- a/bumblebee_status/modules/contrib/apt.py +++ b/bumblebee_status/modules/contrib/apt.py @@ -14,6 +14,7 @@ import threading import core.module import core.widget import core.decorators +import core.input import util.cli @@ -56,6 +57,8 @@ class Module(core.module.Module): def __init__(self, config, theme): super().__init__(config, theme, core.widget.Widget(self.updates)) self.__thread = None + core.input.register(self, button=core.input.RIGHT_MOUSE, + cmd=self.updates) def updates(self, widget): if widget.get("error"): From 8bde6378d4db1ae17f749bc3c5c7559184cacb94 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Fri, 14 Jan 2022 13:29:29 +0100 Subject: [PATCH 074/301] [modules/arandr] handle case of "no layouts exist To ensure that arandr works also if no layouts are available, add some (very simplistic) exception handling. see #844 --- bumblebee_status/modules/contrib/arandr.py | 23 ++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/bumblebee_status/modules/contrib/arandr.py b/bumblebee_status/modules/contrib/arandr.py index 7af565d..fd9ad9e 100644 --- a/bumblebee_status/modules/contrib/arandr.py +++ b/bumblebee_status/modules/contrib/arandr.py @@ -136,16 +136,19 @@ class Module(core.module.Module): def _get_layouts(): """Loads and parses the arandr screen layout scripts.""" layouts = {} - for filename in os.listdir(__screenlayout_dir__): - if fnmatch.fnmatch(filename, '*.sh'): - fullpath = os.path.join(__screenlayout_dir__, filename) - with open(fullpath, "r") as file: - for line in file: - s_line = line.strip() - if "xrandr" not in s_line: - continue - displays_in_file = Module._parse_layout(line) - layouts[filename] = displays_in_file + try: + for filename in os.listdir(__screenlayout_dir__): + if fnmatch.fnmatch(filename, '*.sh'): + fullpath = os.path.join(__screenlayout_dir__, filename) + with open(fullpath, "r") as file: + for line in file: + s_line = line.strip() + if "xrandr" not in s_line: + continue + displays_in_file = Module._parse_layout(line) + layouts[filename] = displays_in_file + except Exception as e: + log.error(str(e)) return layouts @staticmethod From 08b5386140fb14ecbccdf9eb7520f597967dee4d Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Fri, 14 Jan 2022 13:39:04 +0100 Subject: [PATCH 075/301] [util/popup] fix endless loop on "close on leave" When closing a popup window when the mouse leave the area (default behaviour, unfortunately), the main "show()" got stuck in an infinite loop. Fix that by setting running to False when exiting. fixes #844 --- bumblebee_status/modules/contrib/arandr.py | 11 ++++++----- bumblebee_status/util/popup.py | 1 + 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/bumblebee_status/modules/contrib/arandr.py b/bumblebee_status/modules/contrib/arandr.py index fd9ad9e..b207524 100644 --- a/bumblebee_status/modules/contrib/arandr.py +++ b/bumblebee_status/modules/contrib/arandr.py @@ -54,7 +54,7 @@ class Module(core.module.Module): def activate_layout(layout_path): log.debug("activating layout") log.debug(layout_path) - execute(layout_path) + execute(layout_path, ignore_errors=True) def popup(self, widget): """Create Popup that allows the user to control their displays in one @@ -64,7 +64,7 @@ class Module(core.module.Module): menu = popup.menu() menu.add_menuitem( "arandr", - callback=partial(execute, self.manager) + callback=partial(execute, self.manager, ignore_errors=True) ) menu.add_separator() @@ -105,11 +105,12 @@ class Module(core.module.Module): if count_on == 1: log.info("attempted to turn off last display") return - execute("{} --output {} --off".format(self.toggle_cmd, display)) + execute("{} --output {} --off".format(self.toggle_cmd, display), ignore_errors=True) else: log.debug("toggling on {}".format(display)) execute( - "{} --output {} --auto".format(self.toggle_cmd, display) + "{} --output {} --auto".format(self.toggle_cmd, display), + ignore_errors=True ) @staticmethod @@ -120,7 +121,7 @@ class Module(core.module.Module): connected). """ displays = {} - for line in execute("xrandr -q").split("\n"): + for line in execute("xrandr -q", ignore_errors=True).split("\n"): if "connected" not in line: continue is_on = bool(re.search(r"\d+x\d+\+(\d+)\+\d+", line)) diff --git a/bumblebee_status/util/popup.py b/bumblebee_status/util/popup.py index bbabe66..784a037 100644 --- a/bumblebee_status/util/popup.py +++ b/bumblebee_status/util/popup.py @@ -49,6 +49,7 @@ class menu(object): return self._menu def __on_focus_out(self, event=None): + self.running = False self._root.destroy() def __on_click(self, callback): From 8867f4f188141a07d3e6d8ee157472cf99a60129 Mon Sep 17 00:00:00 2001 From: Dhananjay Tanpure Date: Thu, 20 Jan 2022 21:06:17 +0000 Subject: [PATCH 076/301] added module for blugon --- bumblebee_status/modules/contrib/blugon.py | 97 ++++++++++++++++++++++ tests/modules/contrib/test_blugon.py | 2 + 2 files changed, 99 insertions(+) create mode 100644 bumblebee_status/modules/contrib/blugon.py create mode 100644 tests/modules/contrib/test_blugon.py diff --git a/bumblebee_status/modules/contrib/blugon.py b/bumblebee_status/modules/contrib/blugon.py new file mode 100644 index 0000000..4fa0079 --- /dev/null +++ b/bumblebee_status/modules/contrib/blugon.py @@ -0,0 +1,97 @@ +"""Displays temperature of blugon and Controls it. + +Use wheel up and down to change temperature, middle click to toggle and right click to reset temprature. + +Default Values: + * Minimum temperature: 1000 (red) + * Maximum temperature: 20000 (blue) + * Default temperature: 6600 + +Requires the following executable: + * blugon + +Parameters: + * blugon.step: The amount of increase/decrease on scroll (default: 200) + +contributed by `DTan13 ` +""" + +import core.module +import core.widget + +import util.cli +import util.format + +import os + + +class Module(core.module.Module): + def __init__(self, config, theme): + super().__init__(config, theme, core.widget.Widget(self.full_text)) + self.__state = True + self.__default = 6600 + self.__step = ( + util.format.asint(self.parameter("step")) if self.parameter("step") else 200 + ) + self.__max, self.__min = 20000, 1000 + + file = open(os.path.expanduser("~/.config/blugon/current")) + self.__current = int(float(file.read())) + + events = [ + { + "type": "toggle", + "action": self.toggle, + "button": core.input.MIDDLE_MOUSE, + }, + { + "type": "blue", + "action": self.blue, + "button": core.input.WHEEL_UP, + }, + { + "type": "red", + "action": self.red, + "button": core.input.WHEEL_DOWN, + }, + { + "type": "reset", + "action": self.reset, + "button": core.input.RIGHT_MOUSE, + }, + ] + + for event in events: + core.input.register(self, button=event["button"], cmd=event["action"]) + + def set_temp(self): + temp = self.__current if self.__state else self.__default + util.cli.execute("blugon --setcurrent={}".format(temp)) + + def full_text(self, widget): + return self.__current if self.__state else self.__default + + def state(self, widget): + if not self.__state: + return ["critical"] + + def toggle(self, event): + self.__state = not self.__state + self.set_temp() + + def reset(self, event): + self.__current = 6600 + self.set_temp() + + def blue(self, event): + if self.__state and (self.__current < self.__max): + self.__current += self.__step + self.set_temp() + + def red(self, event): + if self.__state and (self.__current > self.__min): + self.__current -= self.__step + self.set_temp() + + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/tests/modules/contrib/test_blugon.py b/tests/modules/contrib/test_blugon.py new file mode 100644 index 0000000..6c9dd9b --- /dev/null +++ b/tests/modules/contrib/test_blugon.py @@ -0,0 +1,2 @@ +def test_load_module(): + __import__("modules.contrib.zpool") From c40a1744637b84682731d015420456d35b004182 Mon Sep 17 00:00:00 2001 From: Logan Connolly Date: Tue, 25 Jan 2022 17:50:25 +0100 Subject: [PATCH 077/301] feat(theme): add rose pine theme --- themes/rose-pine.json | 54 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 themes/rose-pine.json diff --git a/themes/rose-pine.json b/themes/rose-pine.json new file mode 100644 index 0000000..e1ce49b --- /dev/null +++ b/themes/rose-pine.json @@ -0,0 +1,54 @@ +{ + "icons": ["awesome-fonts"], + "defaults": { + "separator-block-width": 0, + "warning": { + "fg": "#232136", + "bg": "#f6c177" + }, + "critical": { + "fg": "#232136", + "bg": "#eb6f92" + } + }, + "cycle": [ + { "fg": "#232136", "bg": "#ea9a97" }, + { "fg": "#e0def4", "bg": "#393552" } + ], + "dnf": { + "good": { + "fg": "#232136", + "bg": "#9ccfd8" + } + }, + "pacman": { + "good": { + "fg": "#232136", + "bg": "#9ccfd8" + } + }, + "battery": { + "charged": { + "fg": "#232136", + "bg": "#9ccfd8" + }, + "AC": { + "fg": "#232136", + "bg": "#9ccfd8" + } + }, + "pomodoro": { + "paused": { + "fg": "#232136", + "bg": "#f6c177" + }, + "work": { + "fg": "#232136", + "bg": "#9ccfd8" + }, + "break": { + "fg": "#232136", + "bg": "#c4a7e7" + } + } +} From 4c08cd812e7c436113c7e88245ea929dc35fef44 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Wed, 9 Feb 2022 21:15:08 +0100 Subject: [PATCH 078/301] Create codeql-analysis.yml Trying out CodeQL --- .github/workflows/codeql-analysis.yml | 70 +++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 .github/workflows/codeql-analysis.yml diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..5e39422 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,70 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ main ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ main ] + schedule: + - cron: '31 0 * * 4' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'python' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://git.io/codeql-language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 From 8458eef1e60fa19d862977b2b08c484e0261d55c Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Wed, 9 Feb 2022 21:24:35 +0100 Subject: [PATCH 079/301] [doc] add codeql badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 59533bc..ba6261f 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ [![Code Climate](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status/badges/gpa.svg)](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status) [![Test Coverage](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status/badges/coverage.svg)](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status/coverage) [![Issue Count](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status/badges/issue_count.svg)](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status) +[![CodeQL](https://github.com/tobi-wan-kenobi/bumblebee-status/actions/workflows/codeql-analysis.yml/badge.svg?branch=main)](https://github.com/tobi-wan-kenobi/bumblebee-status/actions/workflows/codeql-analysis.yml) ![License](https://img.shields.io/github/license/tobi-wan-kenobi/bumblebee-status) **Many, many thanks to all contributors! I am still amazed by and deeply grateful for how many PRs this project gets.** From 4f9553f7ea4ca9d9166980384669c451b74cd019 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Fri, 11 Feb 2022 13:44:10 +0100 Subject: [PATCH 080/301] [modules/rss] fix insecure use of tempfile fixes #850 --- bumblebee_status/modules/contrib/rss.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bumblebee_status/modules/contrib/rss.py b/bumblebee_status/modules/contrib/rss.py index 7b8c032..7824e2e 100644 --- a/bumblebee_status/modules/contrib/rss.py +++ b/bumblebee_status/modules/contrib/rss.py @@ -55,7 +55,7 @@ class Module(core.module.Module): self._state = [] - self._newspaper_filename = tempfile.mktemp(".html") + self._newspaper_file = tempfile.NamedTemporaryFile(mode="w", suffix=".html") self._last_refresh = 0 self._last_update = 0 @@ -308,10 +308,11 @@ class Module(core.module.Module): while newspaper_items: content += self._create_news_section(newspaper_items) - open(self._newspaper_filename, "w").write( + self._newspaper_file.write( HTML_TEMPLATE.replace("[[CONTENT]]", content) ) - webbrowser.open("file://" + self._newspaper_filename) + self._newspaper_file.flush() + webbrowser.open("file://" + self._newspaper_file.name) self._update_history("newspaper") self._save_history() From 5c390be25cba3338c858e0fb77e4d446515fd5b1 Mon Sep 17 00:00:00 2001 From: Christopher Kepes Date: Sat, 12 Feb 2022 11:06:10 +0100 Subject: [PATCH 081/301] [modules/nic] Added strength indicator for wifi signals --- bumblebee_status/modules/core/nic.py | 46 ++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/bumblebee_status/modules/core/nic.py b/bumblebee_status/modules/core/nic.py index 7dde2f0..591fe1c 100644 --- a/bumblebee_status/modules/core/nic.py +++ b/bumblebee_status/modules/core/nic.py @@ -13,7 +13,9 @@ Parameters: * nic.exclude: Comma-separated list of interface prefixes (supporting regular expressions) to exclude (defaults to 'lo,virbr,docker,vboxnet,veth,br,.*:avahi') * nic.include: Comma-separated list of interfaces to include * nic.states: Comma-separated list of states to show (prefix with '^' to invert - i.e. ^down -> show all devices that are not in state down) - * nic.format: Format string (defaults to '{intf} {state} {ip} {ssid}') + * nic.format: Format string (defaults to '{intf} {state} {ip} {ssid} {strength}') + * nic.strength_warning: Integer to set the threshold for warning state (defaults to 50) + * nic.strength_critical: Integer to set the threshold for critical state (defaults to 30) """ import re @@ -28,7 +30,7 @@ import util.format class Module(core.module.Module): - @core.decorators.every(seconds=10) + @core.decorators.every(seconds=5) def __init__(self, config, theme): widgets = [] super().__init__(config, theme, widgets) @@ -45,7 +47,15 @@ class Module(core.module.Module): self._states["exclude"].append(state[1:]) else: self._states["include"].append(state) - self._format = self.parameter("format", "{intf} {state} {ip} {ssid}") + self._format = self.parameter("format", "{intf} {state} {ip} {ssid} {strength}") + + self._strength_threshold_critical = self.parameter("strength_critical", 30) + self._strength_threshold_warning = self.parameter("strength_warning", 50) + + # Limits for the accepted dBm values of wifi strength + self.__strength_dbm_lower_bound = -110 + self.__strength_dbm_upper_bound = -30 + self.iw = shutil.which("iw") self._update_widgets(widgets) @@ -64,6 +74,13 @@ class Module(core.module.Module): iftype = "wireless" if self._iswlan(intf) else "wired" iftype = "tunnel" if self._istunnel(intf) else iftype + # "strength" is none if interface type is not wlan + if self._iswlan(intf): + if widget.get("strength") < self._strength_threshold_critical: + states.append("critical") + elif widget.get("strength") < self._strength_threshold_warning: + states.append("warning") + states.append("{}-{}".format(iftype, widget.get("state"))) return states @@ -116,6 +133,9 @@ class Module(core.module.Module): ): continue + strength_dbm = self.get_strength_dbm(intf) + strength_percent = self.convert_strength_dbm_percent(strength_dbm) + widget = self.widget(intf) if not widget: widget = self.add_widget(name=intf) @@ -126,12 +146,14 @@ class Module(core.module.Module): ip=", ".join(addr), intf=intf, state=state, + strength=str(strength_percent) + "%" if strength_percent else "", ssid=self.get_ssid(intf), ).split() ) ) widget.set("intf", intf) widget.set("state", state) + widget.set("strength", strength_percent) def get_ssid(self, intf): if not self._iswlan(intf) or self._istunnel(intf) or not self.iw: @@ -145,5 +167,23 @@ class Module(core.module.Module): return "" + def get_strength_dbm(self, intf): + if not self._iswlan(intf) or self._istunnel(intf) or not self.iw: + return None + + with open("/proc/net/wireless", "r") as file: + for line in file: + if intf in line: + # Remove trailing . by slicing it off ;) + strength_dbm = line.split()[3][:-1] + return util.format.asint(strength_dbm, + minium=self.__strength_dbm_lower_bound, + maximum=self.__strength_dbm_upper_bound) + + return None + + def convert_strength_dbm_percent(self, signal): + return int(100 * ((signal + 100) / 70.0)) if signal else None + # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From 4784be40762601c656ce8964c15c11d556ab8b0d Mon Sep 17 00:00:00 2001 From: Pi-Yueh Chuang Date: Sat, 12 Feb 2022 12:54:31 -0500 Subject: [PATCH 082/301] typo in nic.py: minium -> minimum --- bumblebee_status/modules/core/nic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bumblebee_status/modules/core/nic.py b/bumblebee_status/modules/core/nic.py index 591fe1c..94a9e20 100644 --- a/bumblebee_status/modules/core/nic.py +++ b/bumblebee_status/modules/core/nic.py @@ -177,7 +177,7 @@ class Module(core.module.Module): # Remove trailing . by slicing it off ;) strength_dbm = line.split()[3][:-1] return util.format.asint(strength_dbm, - minium=self.__strength_dbm_lower_bound, + minimum=self.__strength_dbm_lower_bound, maximum=self.__strength_dbm_upper_bound) return None From 2a77e3a85c1f59318a7538028314e0edde9c3b57 Mon Sep 17 00:00:00 2001 From: Mihai Morariu Date: Mon, 14 Feb 2022 15:36:24 +0200 Subject: [PATCH 083/301] Fix exception in location.py. --- bumblebee_status/modules/contrib/sun.py | 10 +++++++++- bumblebee_status/util/location.py | 4 ++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/bumblebee_status/modules/contrib/sun.py b/bumblebee_status/modules/contrib/sun.py index e9eefd2..34a4b71 100644 --- a/bumblebee_status/modules/contrib/sun.py +++ b/bumblebee_status/modules/contrib/sun.py @@ -39,7 +39,11 @@ class Module(core.module.Module): self.__sun = None if not lat or not lon: - lat, lon = util.location.coordinates() + try: + lat, lon = util.location.coordinates() + except Exception: + pass + if lat and lon: self.__sun = Sun(float(lat), float(lon)) @@ -55,6 +59,10 @@ class Module(core.module.Module): return "n/a" def __calculate_times(self): + if not self.__sun: + self.__sunset = self.__sunrise = None + return + self.__isup = False order_matters = True diff --git a/bumblebee_status/util/location.py b/bumblebee_status/util/location.py index 12242ea..e48b71a 100644 --- a/bumblebee_status/util/location.py +++ b/bumblebee_status/util/location.py @@ -59,11 +59,11 @@ def __load(): __next = time.time() + 60 * 30 # error - try again every 30m -def __get(name, default=None): +def __get(name): global __data if not __data or __expired(): __load() - return __data.get(name, default) + return __data[name] def reset(): From 3aadab5628f0b4f839428231e28f1d7ab9d60d0c Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Mon, 14 Feb 2022 14:58:01 +0100 Subject: [PATCH 084/301] [modules/publicip] handle missing public ip more gracefully If location does not throw, but reports an empty public IP, return "n/a". Since this caused a bug, also add a test for it. fixes #853 --- bumblebee_status/modules/contrib/publicip.py | 4 ++-- tests/modules/contrib/test_publicip.py | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/bumblebee_status/modules/contrib/publicip.py b/bumblebee_status/modules/contrib/publicip.py index 17a23e3..a74d708 100644 --- a/bumblebee_status/modules/contrib/publicip.py +++ b/bumblebee_status/modules/contrib/publicip.py @@ -16,13 +16,13 @@ class Module(core.module.Module): self.__ip = "" def public_ip(self, widget): - return self.__ip + return self.__ip or "n/a" def update(self): try: self.__ip = util.location.public_ip() except Exception: - self.__ip = "n/a" + self.__ip = None # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/tests/modules/contrib/test_publicip.py b/tests/modules/contrib/test_publicip.py index 9584bd7..870ede6 100644 --- a/tests/modules/contrib/test_publicip.py +++ b/tests/modules/contrib/test_publicip.py @@ -26,6 +26,15 @@ class PublicIPTest(TestCase): assert widget(module).full_text() == '5.12.220.2' + @mock.patch('util.location.public_ip') + def test_public_ip(self, public_ip_mock): + public_ip_mock.return_value = None + + module = build_module() + module.update() + + assert widget(module).full_text() == 'n/a' + @mock.patch('util.location.public_ip') def test_public_ip_with_exception(self, public_ip_mock): public_ip_mock.side_effect = Exception From 03731136b6f2147ded54ce2881eddc4b1dba5d83 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Tue, 15 Feb 2022 16:34:02 +0100 Subject: [PATCH 085/301] [modules/nic] fix missing check for None --- bumblebee_status/modules/core/nic.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bumblebee_status/modules/core/nic.py b/bumblebee_status/modules/core/nic.py index 94a9e20..09fe487 100644 --- a/bumblebee_status/modules/core/nic.py +++ b/bumblebee_status/modules/core/nic.py @@ -75,10 +75,11 @@ class Module(core.module.Module): iftype = "tunnel" if self._istunnel(intf) else iftype # "strength" is none if interface type is not wlan - if self._iswlan(intf): - if widget.get("strength") < self._strength_threshold_critical: + strength = widget.get("strength") + if self._iswlan(intf) and strength: + if strength < self._strength_threshold_critical: states.append("critical") - elif widget.get("strength") < self._strength_threshold_warning: + elif strength < self._strength_threshold_warning: states.append("warning") states.append("{}-{}".format(iftype, widget.get("state"))) From 928f8258aabf5cb05b709fa3740099218d5acae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20L=C3=BCftinger?= Date: Sun, 20 Feb 2022 12:29:49 +0100 Subject: [PATCH 086/301] fix case of Kelvin SI unit in redshift widget --- bumblebee_status/modules/core/redshift.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bumblebee_status/modules/core/redshift.py b/bumblebee_status/modules/core/redshift.py index c6735b1..d50463f 100644 --- a/bumblebee_status/modules/core/redshift.py +++ b/bumblebee_status/modules/core/redshift.py @@ -54,7 +54,7 @@ def get_redshift_value(module): for line in res.split("\n"): line = line.lower() if "temperature" in line: - widget.set("temp", line.split(" ")[2]) + widget.set("temp", line.split(" ")[2].upper()) if "period" in line: state = line.split(" ")[1] if "day" in state: From 950931e1b9e9619d556a57e3a576e2dc1b6bbc51 Mon Sep 17 00:00:00 2001 From: Bernhard B Date: Mon, 28 Feb 2022 19:14:21 +0100 Subject: [PATCH 087/301] added new module 'pactl' * added new module 'pactl' which displays the current default sink and allows to select a different default sink from the popup menu. --- bumblebee_status/modules/contrib/pactl.py | 142 ++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 bumblebee_status/modules/contrib/pactl.py diff --git a/bumblebee_status/modules/contrib/pactl.py b/bumblebee_status/modules/contrib/pactl.py new file mode 100644 index 0000000..0abea68 --- /dev/null +++ b/bumblebee_status/modules/contrib/pactl.py @@ -0,0 +1,142 @@ +# pylint: disable=C0111,R0903 + +""" Displays the current default sink. + + Left click opens a popup menu that lists all available sinks and allows to change the default sink. + + Per default, this module uses the sink names returned by "pactl list sinks short" + + sample output of "pactl list sinks short": + + 2 alsa_output.pci-0000_00_1f.3.analog-stereo module-alsa-card.c s16le 2ch 44100Hz SUSPENDED + 3 alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo module-alsa-card.c s16le 2ch 44100Hz SUSPENDE + + As "alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo" is not a particularly nice name, its possible to map the name to more a + user friendly name. e.g to map "alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo" to the name "Headset", add the following + bumblebee-status config entry: pactl.alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo=Headset + + The module also allows to specify individual (unicode) icons for all the sinks. e.g in order to use the icon 🎧 for the + "alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo" sink, add the following bumblebee-status config entry: + pactl.icon.alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo=🎧 + + Requirements: + * pulseaudio + * pactl +""" + +import logging +import functools + +import core.module +import core.widget +import core.input + +import util.cli +import util.popup + + +class Sink(object): + def __init__(self, id, internal_name, friendly_name, icon): + super().__init__() + self.__id = id + self.__internal_name = internal_name + self.__friendly_name = friendly_name + self.__icon = icon + + @property + def id(self): + return self.__id + + @property + def internal_name(self): + return self.__internal_name + + @property + def friendly_name(self): + return self.__friendly_name + + @property + def icon(self): + return self.__icon + + @property + def display_name(self): + display_name = ( + self.__icon + " " + self.__friendly_name + if self.__icon != "" + else self.__friendly_name + ) + return display_name + + +class Module(core.module.Module): + def __init__(self, config, theme): + super().__init__(config, theme, core.widget.Widget(self.default_sink)) + + self.__default_sink = None + + res = util.cli.execute("pactl list sinks short") + lines = res.splitlines() + + self.__sinks = [] + for line in lines: + info = line.split("\t") + try: + friendly_name = self.parameter(info[1], info[1]) + icon = self.parameter("icon." + info[1], "") + self.__sinks.append(Sink(info[0], info[1], friendly_name, icon)) + except: + logging.exception("Couldn't parse sink") + pass + + core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.popup) + + def __sink(self, internal_sink_name): + for sink in self.__sinks: + logging.info(sink.internal_name) + if internal_sink_name == sink.internal_name: + return sink + return None + + def update(self): + try: + res = util.cli.execute("pactl info") + lines = res.splitlines() + self.__default_sink = None + for line in lines: + if not line.startswith("Default Sink:"): + continue + internal_sink_name = line.replace("Default Sink: ", "") + self.__default_sink = self.__sink(internal_sink_name) + break + except Exception as e: + logging.exception("Could not get pactl info") + self.__default_sink = None + + def default_sink(self, widget): + if self.__default_sink is None: + return "unknown" + return self.__default_sink.display_name + + def __on_sink_selected(self, sink): + try: + util.cli.execute("pactl set-default-sink {}".format(sink.id)) + self.__default_sink = sink + except Exception as e: + logging.exception("Couldn't set default sink") + + def popup(self, widget): + menu = util.popup.menu() + + for sink in self.__sinks: + menu.add_menuitem( + sink.friendly_name, + callback=functools.partial(self.__on_sink_selected, sink), + ) + menu.show(widget) + + def state(self, widget): + return [] + + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From 33d22c2637f0b667182b9b0c3a7b7db2b60578fd Mon Sep 17 00:00:00 2001 From: Bernhard B Date: Mon, 28 Feb 2022 19:20:19 +0100 Subject: [PATCH 088/301] removed debug log from 'pactl' module --- bumblebee_status/modules/contrib/pactl.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bumblebee_status/modules/contrib/pactl.py b/bumblebee_status/modules/contrib/pactl.py index 0abea68..cd62c9a 100644 --- a/bumblebee_status/modules/contrib/pactl.py +++ b/bumblebee_status/modules/contrib/pactl.py @@ -93,7 +93,6 @@ class Module(core.module.Module): def __sink(self, internal_sink_name): for sink in self.__sinks: - logging.info(sink.internal_name) if internal_sink_name == sink.internal_name: return sink return None From 07200c466bd746b4076db0547093f467d8c70254 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Wed, 2 Mar 2022 12:33:02 +0100 Subject: [PATCH 089/301] [themes] add zengarden light (powerline) --- themes/zengarden-powerline-light.json | 64 +++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 themes/zengarden-powerline-light.json diff --git a/themes/zengarden-powerline-light.json b/themes/zengarden-powerline-light.json new file mode 100644 index 0000000..ecf7de5 --- /dev/null +++ b/themes/zengarden-powerline-light.json @@ -0,0 +1,64 @@ +{ + "icons": [ "paxy97", "awesome-fonts" ], + "defaults": { + "warning": { + "fg": "#353839", + "bg": "#967117" + }, + "critical": { + "fg": "#353839", + "bg": "#ba1d58" + }, + "default-separators": false, + "separator-block-width": 0 + }, + "fg": "#353839", + "bg": "#c4b6a3", + "dnf": { + "good": { + "fg": "#353839", + "bg": "#177245" + } + }, + "apt": { + "good": { + "fg": "#353839", + "bg": "#177245" + } + }, + "battery": { + "charged": { + "fg": "#353839", + "bg": "#177245" + }, + "AC": { + "fg": "#353839", + "bg": "#177245" + } + }, + "bluetooth": { + "ON": { + "fg": "#353839", + "bg": "#177245" + } + }, + "git": { + "modified": { "bg": "#174572" }, + "deleted": { "bg": "#ba1d58" }, + "new": { "bg": "#967117" } + }, + "pomodoro": { + "paused": { + "fg": "#353839", + "bg": "#d79921" + }, + "work": { + "fg": "#353839", + "bg": "#177245" + }, + "break": { + "fg": "#353839", + "bg": "#177245" + } + } +} From 9a2e7637c9d5c6d4098b3bcff548dbf4045d31a2 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Wed, 2 Mar 2022 16:16:24 +0100 Subject: [PATCH 090/301] [themes/zengarden] lighter accents --- themes/zengarden-powerline-light.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/themes/zengarden-powerline-light.json b/themes/zengarden-powerline-light.json index ecf7de5..7fe0bc5 100644 --- a/themes/zengarden-powerline-light.json +++ b/themes/zengarden-powerline-light.json @@ -3,11 +3,11 @@ "defaults": { "warning": { "fg": "#353839", - "bg": "#967117" + "bg": "#b38a32" }, "critical": { "fg": "#353839", - "bg": "#ba1d58" + "bg": "#d94070" }, "default-separators": false, "separator-block-width": 0 @@ -17,29 +17,29 @@ "dnf": { "good": { "fg": "#353839", - "bg": "#177245" + "bg": "#378c5d" } }, "apt": { "good": { "fg": "#353839", - "bg": "#177245" + "bg": "#378c5d" } }, "battery": { "charged": { "fg": "#353839", - "bg": "#177245" + "bg": "#378c5d" }, "AC": { "fg": "#353839", - "bg": "#177245" + "bg": "#378c5d" } }, "bluetooth": { "ON": { "fg": "#353839", - "bg": "#177245" + "bg": "#378c5d" } }, "git": { @@ -54,11 +54,11 @@ }, "work": { "fg": "#353839", - "bg": "#177245" + "bg": "#378c5d" }, "break": { "fg": "#353839", - "bg": "#177245" + "bg": "#378c5d" } } } From c57daf65ce6da29d405570e1de09adbc9bad779b Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Thu, 3 Mar 2022 14:31:47 +0100 Subject: [PATCH 091/301] [themes/zengarden] add key colors --- themes/zengarden-powerline-light.json | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/themes/zengarden-powerline-light.json b/themes/zengarden-powerline-light.json index 7fe0bc5..097842a 100644 --- a/themes/zengarden-powerline-light.json +++ b/themes/zengarden-powerline-light.json @@ -60,5 +60,19 @@ "fg": "#353839", "bg": "#378c5d" } - } + }, + "keys": { + "Key.cmd": { + "bg": "#477ab7" + }, + "Key.shift": { + "bg": "#b38a32" + }, + "Key.ctrl": { + "bg": "#377c8b" + }, + "Key.alt": { + "bg": "#e05b1f" + } + } } From 7d33171749b0109ad55bb88b6dafc9b011b12f83 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Fri, 4 Mar 2022 09:35:43 +0100 Subject: [PATCH 092/301] [core/input] methods can be event callbacks When registering an event (especially mouse events), if the parameter is a valid method in the Module, execute that with the event as parameter. Add this in the core.spacer module as an example. fixes #858 see #857 --- bumblebee_status/core/input.py | 3 +++ bumblebee_status/modules/core/spacer.py | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/bumblebee_status/core/input.py b/bumblebee_status/core/input.py index 2f9fdfc..5752dd8 100644 --- a/bumblebee_status/core/input.py +++ b/bumblebee_status/core/input.py @@ -54,8 +54,11 @@ def register(obj, button=None, cmd=None, wait=False): event_id = __event_id(obj.id if obj is not None else "", button) logging.debug("registering callback {}".format(event_id)) core.event.unregister(event_id) # make sure there's always only one input event + if callable(cmd): core.event.register_exclusive(event_id, cmd) + elif obj and hasattr(obj, cmd) and callable(getattr(obj, cmd)): + core.event.register_exclusive(event_id, lambda event: getattr(obj, cmd)(event)) else: core.event.register_exclusive(event_id, lambda event: __execute(event, cmd, wait)) diff --git a/bumblebee_status/modules/core/spacer.py b/bumblebee_status/modules/core/spacer.py index 7e4453a..e5a2d5e 100644 --- a/bumblebee_status/modules/core/spacer.py +++ b/bumblebee_status/modules/core/spacer.py @@ -9,7 +9,7 @@ Parameters: import core.module import core.widget import core.decorators - +import core.input class Module(core.module.Module): @core.decorators.every(minutes=60) @@ -20,5 +20,8 @@ class Module(core.module.Module): def text(self, _): return self.__text + def update_text(self, event): + self.__text = core.input.button_name(event["button"]) + # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From 9cbc39e462df8b2f732d22c1821938fef680bc4f Mon Sep 17 00:00:00 2001 From: Sadegh Hamedani Date: Fri, 4 Mar 2022 18:28:13 +0330 Subject: [PATCH 093/301] [contrib] added module persian_date --- .../modules/contrib/persian_date.py | 45 +++++++++++++++++++ themes/icons/awesome-fonts.json | 1 + 2 files changed, 46 insertions(+) create mode 100644 bumblebee_status/modules/contrib/persian_date.py diff --git a/bumblebee_status/modules/contrib/persian_date.py b/bumblebee_status/modules/contrib/persian_date.py new file mode 100644 index 0000000..6e3eded --- /dev/null +++ b/bumblebee_status/modules/contrib/persian_date.py @@ -0,0 +1,45 @@ +# pylint: disable=C0111,R0903 + +"""Displays the current date and time in Persian(Jalali) Calendar. + +Requires the following python packages: + * jdatetime + +Parameters: + * datetime.format: strftime()-compatible formatting string. default: "%A %d %B" e.g., "جمعه ۱۳ اسفند" + * datetime.locale: locale to use. default: "fa_IR" +""" + +from __future__ import absolute_import +import jdatetime +import locale + +import core.module +import core.widget +import core.input + + +class Module(core.module.Module): + def __init__(self, config, theme): + super().__init__(config, theme, core.widget.Widget(self.full_text)) + + l = ("fa_IR", "UTF-8") + lcl = self.parameter("locale", ".".join(l)) + try: + locale.setlocale(locale.LC_ALL, lcl.split(".")) + except Exception as e: + locale.setlocale(locale.LC_ALL, ("fa_IR", "UTF-8")) + + def default_format(self): + return "%A %d %B" + + def full_text(self, widget): + enc = locale.getpreferredencoding() + fmt = self.parameter("format", self.default_format()) + retval = jdatetime.datetime.now().strftime(fmt) + if hasattr(retval, "decode"): + return retval.decode(enc) + return retval + + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/themes/icons/awesome-fonts.json b/themes/icons/awesome-fonts.json index 9f14da9..f85dfd8 100644 --- a/themes/icons/awesome-fonts.json +++ b/themes/icons/awesome-fonts.json @@ -7,6 +7,7 @@ "default-separators": false }, "date": { "prefix": "" }, + "persian_date": { "prefix": "" }, "time": { "prefix": "" }, "datetime": { "prefix": "" }, "datetz": { "prefix": "" }, From e5cdabcc0f9fb972828cf88739072130bf2f545a Mon Sep 17 00:00:00 2001 From: Dhananjay Tanpure Date: Mon, 7 Mar 2022 08:50:30 +0530 Subject: [PATCH 094/301] mpd and title --- bumblebee_status/modules/contrib/mpd.py | 9 +++++++++ bumblebee_status/modules/contrib/title.py | 6 +++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/bumblebee_status/modules/contrib/mpd.py b/bumblebee_status/modules/contrib/mpd.py index 3efffff..92568d1 100644 --- a/bumblebee_status/modules/contrib/mpd.py +++ b/bumblebee_status/modules/contrib/mpd.py @@ -94,6 +94,12 @@ class Module(core.module.Module): "cmd": "mpc toggle" + self._hostcmd, } widget.full_text(self.description) + elif widget_name == "mpd.toggle": + widget_map[widget] = { + "button": core.input.LEFT_MOUSE, + "cmd": "mpc toggle" + self._hostcmd, + } + widget.full_text(self.toggle) elif widget_name == "mpd.next": widget_map[widget] = { "button": core.input.LEFT_MOUSE, @@ -127,6 +133,9 @@ class Module(core.module.Module): def description(self, widget): return string.Formatter().vformat(self._fmt, (), self._tags) + def toggle(self, widget): + return str(util.cli.execute("mpc status %currenttime%/%totaltime%", ignore_errors=True)).strip() + def update(self): self._load_song() diff --git a/bumblebee_status/modules/contrib/title.py b/bumblebee_status/modules/contrib/title.py index 33af47a..055369e 100644 --- a/bumblebee_status/modules/contrib/title.py +++ b/bumblebee_status/modules/contrib/title.py @@ -9,6 +9,7 @@ Parameters: * title.max : Maximum character length for title before truncating. Defaults to 64. * title.placeholder : Placeholder text to be placed if title was truncated. Defaults to '...'. * title.scroll : Boolean flag for scrolling title. Defaults to False + * title.short : Boolean flag for short title. Defaults to False contributed by `UltimatePancake `_ - many thanks! @@ -35,6 +36,7 @@ class Module(core.module.Module): # parsing of parameters self.__scroll = util.format.asbool(self.parameter("scroll", False)) + self.__short = util.format.asbool(self.parameter("short", False)) self.__max = int(self.parameter("max", 64)) self.__placeholder = self.parameter("placeholder", "...") self.__title = "" @@ -66,7 +68,9 @@ class Module(core.module.Module): def __pollTitle(self): """Updating current title.""" try: - self.__full_title = self.__i3.get_tree().find_focused().name + focused = self.__i3.get_tree().find_focused().name + self.__full_title = focused.split( + "-")[-1].strip() if self.__short else focused except: self.__full_title = no_title if self.__full_title is None: From d52f71306381ac02a0a5a768d74f3d045ab17cf0 Mon Sep 17 00:00:00 2001 From: Bernhard B Date: Mon, 7 Mar 2022 20:53:11 +0100 Subject: [PATCH 095/301] Revert "Merge pull request #857 from bbernhard/pactl" This reverts commit eb51a3c1c7d99829304ba615b9caa4ef8417af42, reversing changes made to c57daf65ce6da29d405570e1de09adbc9bad779b. Instead of creating a separate module, the changes will be integrated into the pulseaudio module. --- bumblebee_status/modules/contrib/pactl.py | 141 ---------------------- 1 file changed, 141 deletions(-) delete mode 100644 bumblebee_status/modules/contrib/pactl.py diff --git a/bumblebee_status/modules/contrib/pactl.py b/bumblebee_status/modules/contrib/pactl.py deleted file mode 100644 index cd62c9a..0000000 --- a/bumblebee_status/modules/contrib/pactl.py +++ /dev/null @@ -1,141 +0,0 @@ -# pylint: disable=C0111,R0903 - -""" Displays the current default sink. - - Left click opens a popup menu that lists all available sinks and allows to change the default sink. - - Per default, this module uses the sink names returned by "pactl list sinks short" - - sample output of "pactl list sinks short": - - 2 alsa_output.pci-0000_00_1f.3.analog-stereo module-alsa-card.c s16le 2ch 44100Hz SUSPENDED - 3 alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo module-alsa-card.c s16le 2ch 44100Hz SUSPENDE - - As "alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo" is not a particularly nice name, its possible to map the name to more a - user friendly name. e.g to map "alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo" to the name "Headset", add the following - bumblebee-status config entry: pactl.alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo=Headset - - The module also allows to specify individual (unicode) icons for all the sinks. e.g in order to use the icon 🎧 for the - "alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo" sink, add the following bumblebee-status config entry: - pactl.icon.alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo=🎧 - - Requirements: - * pulseaudio - * pactl -""" - -import logging -import functools - -import core.module -import core.widget -import core.input - -import util.cli -import util.popup - - -class Sink(object): - def __init__(self, id, internal_name, friendly_name, icon): - super().__init__() - self.__id = id - self.__internal_name = internal_name - self.__friendly_name = friendly_name - self.__icon = icon - - @property - def id(self): - return self.__id - - @property - def internal_name(self): - return self.__internal_name - - @property - def friendly_name(self): - return self.__friendly_name - - @property - def icon(self): - return self.__icon - - @property - def display_name(self): - display_name = ( - self.__icon + " " + self.__friendly_name - if self.__icon != "" - else self.__friendly_name - ) - return display_name - - -class Module(core.module.Module): - def __init__(self, config, theme): - super().__init__(config, theme, core.widget.Widget(self.default_sink)) - - self.__default_sink = None - - res = util.cli.execute("pactl list sinks short") - lines = res.splitlines() - - self.__sinks = [] - for line in lines: - info = line.split("\t") - try: - friendly_name = self.parameter(info[1], info[1]) - icon = self.parameter("icon." + info[1], "") - self.__sinks.append(Sink(info[0], info[1], friendly_name, icon)) - except: - logging.exception("Couldn't parse sink") - pass - - core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.popup) - - def __sink(self, internal_sink_name): - for sink in self.__sinks: - if internal_sink_name == sink.internal_name: - return sink - return None - - def update(self): - try: - res = util.cli.execute("pactl info") - lines = res.splitlines() - self.__default_sink = None - for line in lines: - if not line.startswith("Default Sink:"): - continue - internal_sink_name = line.replace("Default Sink: ", "") - self.__default_sink = self.__sink(internal_sink_name) - break - except Exception as e: - logging.exception("Could not get pactl info") - self.__default_sink = None - - def default_sink(self, widget): - if self.__default_sink is None: - return "unknown" - return self.__default_sink.display_name - - def __on_sink_selected(self, sink): - try: - util.cli.execute("pactl set-default-sink {}".format(sink.id)) - self.__default_sink = sink - except Exception as e: - logging.exception("Couldn't set default sink") - - def popup(self, widget): - menu = util.popup.menu() - - for sink in self.__sinks: - menu.add_menuitem( - sink.friendly_name, - callback=functools.partial(self.__on_sink_selected, sink), - ) - menu.show(widget) - - def state(self, widget): - return [] - - -# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From 82ca97c65f7ba6a6c65eef2521f1248859fab66b Mon Sep 17 00:00:00 2001 From: Sadegh Hamedani Date: Thu, 10 Mar 2022 11:52:08 +0330 Subject: [PATCH 096/301] [core/datetime] added 'dtlibrary' attribute and 'set_locale' method --- bumblebee_status/modules/core/datetime.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/bumblebee_status/modules/core/datetime.py b/bumblebee_status/modules/core/datetime.py index febb5fb..0a15c70 100644 --- a/bumblebee_status/modules/core/datetime.py +++ b/bumblebee_status/modules/core/datetime.py @@ -17,26 +17,33 @@ import core.input class Module(core.module.Module): - def __init__(self, config, theme): + def __init__(self, config, theme, dtlibrary=None): super().__init__(config, theme, core.widget.Widget(self.full_text)) core.input.register(self, button=core.input.LEFT_MOUSE, cmd="calendar") - l = locale.getdefaultlocale() + self.dtlibrary = dtlibrary or datetime + + def set_locale(self): + l = self.default_locale() if not l or l == (None, None): l = ("en_US", "UTF-8") lcl = self.parameter("locale", ".".join(l)) try: - locale.setlocale(locale.LC_TIME, lcl.split(".")) + locale.setlocale(locale.LC_ALL, lcl.split(".")) except Exception as e: - locale.setlocale(locale.LC_TIME, ("en_US", "UTF-8")) + locale.setlocale(locale.LC_ALL, ("en_US", "UTF-8")) def default_format(self): return "%x %X" + def default_locale(self): + return locale.getdefaultlocale() + def full_text(self, widget): + self.set_locale() enc = locale.getpreferredencoding() fmt = self.parameter("format", self.default_format()) - retval = datetime.datetime.now().strftime(fmt) + retval = self.dtlibrary.datetime.now().strftime(fmt) if hasattr(retval, "decode"): return retval.decode(enc) return retval From c228ca3b128c420d817664b9bc62dd2db7618ead Mon Sep 17 00:00:00 2001 From: Sadegh Hamedani Date: Thu, 10 Mar 2022 11:53:19 +0330 Subject: [PATCH 097/301] [contrib/persian_date] refactor using `core.datetime` module as parent --- .../modules/contrib/persian_date.py | 28 +++++-------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/bumblebee_status/modules/contrib/persian_date.py b/bumblebee_status/modules/contrib/persian_date.py index 6e3eded..9873e1a 100644 --- a/bumblebee_status/modules/contrib/persian_date.py +++ b/bumblebee_status/modules/contrib/persian_date.py @@ -10,36 +10,22 @@ Parameters: * datetime.locale: locale to use. default: "fa_IR" """ -from __future__ import absolute_import import jdatetime -import locale -import core.module -import core.widget -import core.input +import core.decorators +from modules.core.datetime import Module as dtmodule -class Module(core.module.Module): +class Module(dtmodule): + @core.decorators.every(minutes=1) def __init__(self, config, theme): - super().__init__(config, theme, core.widget.Widget(self.full_text)) - - l = ("fa_IR", "UTF-8") - lcl = self.parameter("locale", ".".join(l)) - try: - locale.setlocale(locale.LC_ALL, lcl.split(".")) - except Exception as e: - locale.setlocale(locale.LC_ALL, ("fa_IR", "UTF-8")) + super().__init__(config, theme, dtlibrary=jdatetime) def default_format(self): return "%A %d %B" - def full_text(self, widget): - enc = locale.getpreferredencoding() - fmt = self.parameter("format", self.default_format()) - retval = jdatetime.datetime.now().strftime(fmt) - if hasattr(retval, "decode"): - return retval.decode(enc) - return retval + def default_locale(self): + return ("fa_IR", "UTF-8") # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From 879744e19cab5cc7357912ba670d200adfd58be6 Mon Sep 17 00:00:00 2001 From: ishaan Date: Mon, 14 Mar 2022 22:28:00 +0530 Subject: [PATCH 098/301] add aur-update --- .../modules/contrib/aur-update.py | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 bumblebee_status/modules/contrib/aur-update.py diff --git a/bumblebee_status/modules/contrib/aur-update.py b/bumblebee_status/modules/contrib/aur-update.py new file mode 100644 index 0000000..c175a63 --- /dev/null +++ b/bumblebee_status/modules/contrib/aur-update.py @@ -0,0 +1,65 @@ +"""Check updates for AUR. + +Requires the following packages: + * yay (used as default) + +Note - You can replace yay by changing the "yay -Qum" +command for your preferred AUR helper. Few examples: + +paru -Qum +pikaur -Qua +rua upgrade --printonly +trizen -Su --aur --quiet +yay -Qum + +contributed by `ishaanbhimwal `_ - many thanks! +""" + +import logging + +import core.module +import core.widget +import core.decorators + +import util.cli + + +class Module(core.module.Module): + @core.decorators.every(minutes=60) + def __init__(self, config, theme): + super().__init__(config, theme, core.widget.Widget(self.utilization)) + self.background = True + self.__packages = 0 + self.__error = False + + @property + def __format(self): + return self.parameter("format", "Update AUR: {}") + + def utilization(self, widget): + return self.__format.format(self.__packages) + + def hidden(self): + return self.__packages == 0 and not self.__error + + def update(self): + self.__error = False + code, result = util.cli.execute( + "yay -Qum", ignore_errors=True, return_exitcode=True + ) + + if code == 0: + self.__packages = len(result.strip().split("\n")) + elif code == 2: + self.__packages = 0 + else: + self.__error = True + logging.error("yay -Qum exited with {}: {}".format(code, result)) + + def state(self, widget): + if self.__error: + return "warning" + return self.threshold_state(self.__packages, 1, 100) + + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From f513582d44d33cc32e71dfb16a8968ca055216db Mon Sep 17 00:00:00 2001 From: ishaan Date: Tue, 15 Mar 2022 01:17:57 +0530 Subject: [PATCH 099/301] fix aur-update showing wrong update number --- .../modules/contrib/aur-update.py | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/bumblebee_status/modules/contrib/aur-update.py b/bumblebee_status/modules/contrib/aur-update.py index c175a63..14bee6b 100644 --- a/bumblebee_status/modules/contrib/aur-update.py +++ b/bumblebee_status/modules/contrib/aur-update.py @@ -1,16 +1,7 @@ """Check updates for AUR. Requires the following packages: - * yay (used as default) - -Note - You can replace yay by changing the "yay -Qum" -command for your preferred AUR helper. Few examples: - -paru -Qum -pikaur -Qua -rua upgrade --printonly -trizen -Su --aur --quiet -yay -Qum + * yay (https://github.com/Jguer/yay) contributed by `ishaanbhimwal `_ - many thanks! """ @@ -49,12 +40,13 @@ class Module(core.module.Module): ) if code == 0: - self.__packages = len(result.strip().split("\n")) - elif code == 2: - self.__packages = 0 + if result == "": + self.__packages = 0 + else: + self.__packages = len(result.strip().split("\n")) else: self.__error = True - logging.error("yay -Qum exited with {}: {}".format(code, result)) + logging.error("aur-update exited with {}: {}".format(code, result)) def state(self, widget): if self.__error: From 1fc4139b7ce0376b80b3f3f71a6f8367609bbe01 Mon Sep 17 00:00:00 2001 From: ishaan Date: Tue, 15 Mar 2022 15:56:42 +0530 Subject: [PATCH 100/301] add documentation and screenshot for aur-update --- .../modules/contrib/aur-update.py | 2 +- docs/modules.rst | 14 + screenshots/arch-update.png | Bin 0 -> 4952 bytes screenshots/aur-update.png | Bin 0 -> 4718 bytes themes/icons/awesome-fonts.json | 759 +++++++++++++----- 5 files changed, 595 insertions(+), 180 deletions(-) create mode 100644 screenshots/arch-update.png create mode 100644 screenshots/aur-update.png diff --git a/bumblebee_status/modules/contrib/aur-update.py b/bumblebee_status/modules/contrib/aur-update.py index 14bee6b..f1b85ea 100644 --- a/bumblebee_status/modules/contrib/aur-update.py +++ b/bumblebee_status/modules/contrib/aur-update.py @@ -1,6 +1,6 @@ """Check updates for AUR. -Requires the following packages: +Requires the following executable: * yay (https://github.com/Jguer/yay) contributed by `ishaanbhimwal `_ - many thanks! diff --git a/docs/modules.rst b/docs/modules.rst index 413f6cf..ad4a307 100644 --- a/docs/modules.rst +++ b/docs/modules.rst @@ -401,6 +401,20 @@ Requires the following executable: contributed by `lucassouto `_ - many thanks! +.. image:: ../screenshots/arch-update.png + +aur-update +~~~~~~~~~~~ + +Check updates for AUR. + +Requires the following executable: + * yay (https://github.com/Jguer/yay) + +contributed by `ishaanbhimwal `_ - many thanks! + +.. image:: ../screenshots/aur-update.png + battery ~~~~~~~ diff --git a/screenshots/arch-update.png b/screenshots/arch-update.png new file mode 100644 index 0000000000000000000000000000000000000000..42b268ee2c5a5fd373576bb71cac277a54b345e7 GIT binary patch literal 4952 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%0DL>SW97vr)abRtKs_E|*`paL#Hq z0|3ji=}e|nD)A*rm5LT@`)L&=UR7_S&(E`)Y)-9F@hx9`xnAJaYTHyQx-O#L;rTC> z%m4jfe**vnLB9C>vcaeyJ~ZU>`M3VRJ~(h(X|cM4{-x&suMHDIK9`P!y=IHE-q<`k zZ8hy|#F3B3qASsCHa9u>n7=eD3FkJb7v_OzGR6Yo#AMDiENYeV+70(_|MSg0 z<)u!?HdmCx>3A_S%OCIf{MX<(?!m*!&#zoE8VsGDj{5b*-v6Smd`M)iZwfw5P-EtP}Z;&wo8Dk(H&Tw;qrC z@W-QXm%V$~A%8%i)!GVEiLD6BI--coCyDq%4x~3W}iT&W~@a z@OK8sajUU70Px|l0KSbc7G2#OESbzkfdv3+eJQK4YBeFYc3?FN-ycO#lS%j{N`l}! z4#MGT4X+-fVLip&Or^pC0HNIK>AJDi7_R|$$2Qs4&LEUHzc}I*dvJhv*#`h-ld+cb z@A&)+c=YITrNR&dap=&H(D%EbO>1n^3a=JXL_`k!v*1#3p>;bV5#a)iC^Su9zWgb# za}-6-Ul^}#KRh7#0|5YVIP8L@JH0{YaJB=0*C$fWAqc|L;jXu!C^9hA4*n*6$Z-dhz=MEXyGX;&7~A13Nqa03=H(E8~5;& z1aW5k)S2;9%C*Yq2ZY{eqt)Bw$N+#DOyZGwE}K#kq+H%YO_gC7KCs*tg+k$Sxhj?N z*x?a-d$TJ>g?wRprrOK3uf*et-rjBu!(1IM6h%=KRjX7T9{0)P$7C{Du~@uy^A1zj zt2Z#vhvSM$g|R!@OQkX|EuB_-;^Z-_#R35L?>|f=)|;-1#iGmYBuUa}Hl&j2QmKUF zcz1W#(IX!-41-}KG?*_GZ)iNxm+&w_4ObKVm6ynsZ^<4#xP8+R%x|unaujqadai>bUO(`+1cq)C=^Vk!m%8R zp(>hoI2}ihj&*uE=EeGLn_+ZxSffz~0)hE?k=&SJ7>m`cqGEzL)$?yQn`|~K z!?J??UMnlH?ye36j;mDa#ZA5XJR_rrdV9NdS}n)1!JxTt#+FVbMhJ-$AyA#Z1!%v>7P>Y`TW6PSTxGo3`LPAPaYRqx8&U@8~_T1 z!Y{wvx^UsFLM|U2-SE?nrE>ZDzkex-<#3oowdv^>Z{EDEw=6Eb z(dl(VLjyLe)%NB3Lj_XF^whIw<7ZCQjRgRhn3(+Z(~A^EUc7j|-v0dgbfv-wK4Cra zc;dy(?7(2ZUaz}+`O}&`99gNqpiAZQ)t~=;?)F?i9~|tjxp*RCnL;zjWSGrn z0RXR0e3uRYc$fWFi>1BYB9mc!O>t-Ee=`hInVftwe)hE8ZnN9lePW+MuJIo`7uSADVKWV9asc2sE}Ko!w5ItV zKl29yZntwc(On}#;mG}a6WpeSlYJtc&MYmx0YDUTxlAUjFia|yT3A@Tefzh~jnMo| z9tZ^Q-Jf7tmLv$dTvn-6!r{n+2a|JRyE_7b;L6Ghj^j9vBM6$yW#?Zn+`4_YQmORy z_14ei0FcRKys(J`qnK2qKruF$`rGCX>!YBGK8|myf5OGIb^W=CRxD{Ep*$ zzddMF{VEg+0|R|1ibkWWwMV$Lbp9L7jt2?&0+q%0tDUzf}QlXFo0E(jgpA<=An5_Q$-6aT$P_!nKNj7={6teA47t0lR zyQ3RxTIaYDKYi+COKn@*^5)IkJ9qAF-GKcb;kTMjO+A~MdM5NqNW2rurJ~>an&12* zVEYiITq=hAi&jThlkE`2Lh<_bUyLSWhudv1>PdpwdiA>@9G5Q?W6{{c;*w}#Swcdh zhFpPL+C4$<{O0EN?L`g%g+eY8d}FqCG%wbb$jUzL&Xtgm5JjmZO;&d}uq05q_!8P? zvYAw?yI3V8B;GL;t={}ph-O6&tv`=KLPFx*v-$Z>Er=WdR%4-bvby(BLPFwTqSISc zn)O!5)M87CCYO+q w_^^;fE+HWyAtCW0gA5Fh32hP*62Bw<52K8BP;%z9+5i9m07*qoM6N<$g0ikSiU0rr literal 0 HcmV?d00001 diff --git a/themes/icons/awesome-fonts.json b/themes/icons/awesome-fonts.json index f85dfd8..3f971d7 100644 --- a/themes/icons/awesome-fonts.json +++ b/themes/icons/awesome-fonts.json @@ -2,187 +2,489 @@ "defaults": { "separator": "", "padding": " ", - "unknown": { "prefix": "" }, + "unknown": { + "prefix": "" + }, "separator-block-width": 0, "default-separators": false }, - "date": { "prefix": "" }, - "persian_date": { "prefix": "" }, - "time": { "prefix": "" }, - "datetime": { "prefix": "" }, - "datetz": { "prefix": "" }, - "timetz": { "prefix": "" }, - "datetimetz": { "prefix": "" }, - "memory": { "prefix": "" }, - "cpu": { "prefix": "" }, - "cpu2": { - "freq": { "prefix": "" }, - "load": { "prefix": "" }, - "loads": { "prefix": "" }, - "temp": { "prefix": "" }, - "fan": { "prefix": "" } + "date": { + "prefix": "" + }, + "persian_date": { + "prefix": "" + }, + "time": { + "prefix": "" + }, + "datetime": { + "prefix": "" + }, + "datetz": { + "prefix": "" + }, + "timetz": { + "prefix": "" + }, + "datetimetz": { + "prefix": "" + }, + "memory": { + "prefix": "" + }, + "cpu": { + "prefix": "" + }, + "cpu2": { + "freq": { + "prefix": "" + }, + "load": { + "prefix": "" + }, + "loads": { + "prefix": "" + }, + "temp": { + "prefix": "" + }, + "fan": { + "prefix": "" + } + }, + "disk": { + "prefix": "" + }, + "smartstatus": { + "prefix": "" + }, + "dnf": { + "prefix": "" + }, + "apt": { + "prefix": "" + }, + "pacman": { + "prefix": "" + }, + "brightness": { + "prefix": "" + }, + "load": { + "prefix": "" + }, + "layout": { + "prefix": "" + }, + "layout-xkb": { + "prefix": "" + }, + "layout-xkbswitch": { + "prefix": "" }, - "disk": { "prefix": "" }, - "smartstatus": { "prefix": "" }, - "dnf": { "prefix": "" }, - "apt": { "prefix": "" }, - "pacman": { "prefix": "" }, - "brightness": { "prefix": "" }, - "load": { "prefix": "" }, - "layout": { "prefix": "" }, - "layout-xkb": { "prefix": "" }, - "layout-xkbswitch": { "prefix": "" }, "notmuch_count": { - "empty": { "prefix": "\uf0e0" }, - "items": { "prefix": "\uf0e0" } + "empty": { + "prefix": "\uf0e0" + }, + "items": { + "prefix": "\uf0e0" + } }, "portage_status": { - "idle": { "prefix": "" }, - "active": { "prefix": "" } + "idle": { + "prefix": "" + }, + "active": { + "prefix": "" + } }, "todo": { - "empty": { "prefix": "" }, - "items": { "prefix": "" }, - "uptime": { "prefix": "" } + "empty": { + "prefix": "" + }, + "items": { + "prefix": "" + }, + "uptime": { + "prefix": "" + } }, "zpool": { - "poolread": { "prefix": "→ " }, - "poolwrite": { "prefix": "← " }, - "ONLINE": { "prefix": "" }, - "FAULTED": { "prefix": "!" }, - "DEGRADED": { "prefix": "!" } + "poolread": { + "prefix": "→ " + }, + "poolwrite": { + "prefix": "← " + }, + "ONLINE": { + "prefix": "" + }, + "FAULTED": { + "prefix": "!" + }, + "DEGRADED": { + "prefix": "!" + } }, "cmus": { - "playing": { "prefix": "" }, - "paused": { "prefix": "" }, - "stopped": { "prefix": "" }, - "prev": { "prefix": "" }, - "next": { "prefix": "" }, - "shuffle-on": { "prefix": "" }, - "shuffle-off": { "prefix": "" }, - "repeat-on": { "prefix": "" }, - "repeat-off": { "prefix": "" } + "playing": { + "prefix": "" + }, + "paused": { + "prefix": "" + }, + "stopped": { + "prefix": "" + }, + "prev": { + "prefix": "" + }, + "next": { + "prefix": "" + }, + "shuffle-on": { + "prefix": "" + }, + "shuffle-off": { + "prefix": "" + }, + "repeat-on": { + "prefix": "" + }, + "repeat-off": { + "prefix": "" + } }, "gpmdp": { - "playing": { "prefix": "" }, - "paused": { "prefix": "" }, - "stopped": { "prefix": "" }, - "prev": { "prefix": "" }, - "next": { "prefix": "" } + "playing": { + "prefix": "" + }, + "paused": { + "prefix": "" + }, + "stopped": { + "prefix": "" + }, + "prev": { + "prefix": "" + }, + "next": { + "prefix": "" + } }, "playerctl": { - "playing": { "prefix": "" }, - "paused": { "prefix": "" }, - "stopped": { "prefix": "" }, - "prev": { "prefix": "" }, - "next": { "prefix": "" } + "playing": { + "prefix": "" + }, + "paused": { + "prefix": "" + }, + "stopped": { + "prefix": "" + }, + "prev": { + "prefix": "" + }, + "next": { + "prefix": "" + } }, "pasink": { - "muted": { "prefix": "" }, - "unmuted": { "prefix": "" } + "muted": { + "prefix": "" + }, + "unmuted": { + "prefix": "" + } }, "amixer": { - "muted": { "prefix": "" }, - "unmuted": { "prefix": "" } + "muted": { + "prefix": "" + }, + "unmuted": { + "prefix": "" + } }, "pasource": { - "muted": { "prefix": "" }, - "unmuted": { "prefix": "" } + "muted": { + "prefix": "" + }, + "unmuted": { + "prefix": "" + } }, "kernel": { "prefix": "\uf17c" }, "nic": { - "wireless-up": { "prefix": "" }, - "wireless-down": { "prefix": "" }, - "wired-up": { "prefix": "" }, - "wired-down": { "prefix": "" }, - "tunnel-up": { "prefix": "" }, - "tunnel-down": { "prefix": "" } + "wireless-up": { + "prefix": "" + }, + "wireless-down": { + "prefix": "" + }, + "wired-up": { + "prefix": "" + }, + "wired-down": { + "prefix": "" + }, + "tunnel-up": { + "prefix": "" + }, + "tunnel-down": { + "prefix": "" + } }, "bluetooth": { - "ON": { "prefix": "" }, - "OFF": { "prefix": "" }, - "?": { "prefix": "" } + "ON": { + "prefix": "" + }, + "OFF": { + "prefix": "" + }, + "?": { + "prefix": "" + } }, "bluetooth2": { - "ON": { "prefix": "" }, - "warning": { "prefix": "" }, - "critical": { "prefix": "" } + "ON": { + "prefix": "" + }, + "warning": { + "prefix": "" + }, + "critical": { + "prefix": "" + } }, "battery-upower": { - "charged": { "prefix": "", "suffix": "" }, - "AC": { "suffix": "" }, - "charging": { - "prefix": ["", "", "", "", ""], + "charged": { + "prefix": "", "suffix": "" }, - "discharging-10": { "prefix": "", "suffix": "" }, - "discharging-25": { "prefix": "", "suffix": "" }, - "discharging-50": { "prefix": "", "suffix": "" }, - "discharging-80": { "prefix": "", "suffix": "" }, - "discharging-100": { "prefix": "", "suffix": "" }, - "unlimited": { "prefix": "", "suffix": "" }, - "estimate": { "prefix": "" }, - "unknown-10": { "prefix": "", "suffix": "" }, - "unknown-25": { "prefix": "", "suffix": "" }, - "unknown-50": { "prefix": "", "suffix": "" }, - "unknown-80": { "prefix": "", "suffix": "" }, - "unknown-100": { "prefix": "", "suffix": "" } + "AC": { + "suffix": "" + }, + "charging": { + "prefix": [ + "", + "", + "", + "", + "" + ], + "suffix": "" + }, + "discharging-10": { + "prefix": "", + "suffix": "" + }, + "discharging-25": { + "prefix": "", + "suffix": "" + }, + "discharging-50": { + "prefix": "", + "suffix": "" + }, + "discharging-80": { + "prefix": "", + "suffix": "" + }, + "discharging-100": { + "prefix": "", + "suffix": "" + }, + "unlimited": { + "prefix": "", + "suffix": "" + }, + "estimate": { + "prefix": "" + }, + "unknown-10": { + "prefix": "", + "suffix": "" + }, + "unknown-25": { + "prefix": "", + "suffix": "" + }, + "unknown-50": { + "prefix": "", + "suffix": "" + }, + "unknown-80": { + "prefix": "", + "suffix": "" + }, + "unknown-100": { + "prefix": "", + "suffix": "" + } }, "battery": { - "charged": { "prefix": "", "suffix": "" }, - "AC": { "suffix": "" }, - "charging": { - "prefix": ["", "", "", "", ""], + "charged": { + "prefix": "", "suffix": "" }, - "discharging-10": { "prefix": "", "suffix": "" }, - "discharging-25": { "prefix": "", "suffix": "" }, - "discharging-50": { "prefix": "", "suffix": "" }, - "discharging-80": { "prefix": "", "suffix": "" }, - "discharging-100": { "prefix": "", "suffix": "" }, - "unlimited": { "prefix": "", "suffix": "" }, - "estimate": { "prefix": "" }, - "unknown-10": { "prefix": "", "suffix": "" }, - "unknown-25": { "prefix": "", "suffix": "" }, - "unknown-50": { "prefix": "", "suffix": "" }, - "unknown-80": { "prefix": "", "suffix": "" }, - "unknown-100": { "prefix": "", "suffix": "" } + "AC": { + "suffix": "" + }, + "charging": { + "prefix": [ + "", + "", + "", + "", + "" + ], + "suffix": "" + }, + "discharging-10": { + "prefix": "", + "suffix": "" + }, + "discharging-25": { + "prefix": "", + "suffix": "" + }, + "discharging-50": { + "prefix": "", + "suffix": "" + }, + "discharging-80": { + "prefix": "", + "suffix": "" + }, + "discharging-100": { + "prefix": "", + "suffix": "" + }, + "unlimited": { + "prefix": "", + "suffix": "" + }, + "estimate": { + "prefix": "" + }, + "unknown-10": { + "prefix": "", + "suffix": "" + }, + "unknown-25": { + "prefix": "", + "suffix": "" + }, + "unknown-50": { + "prefix": "", + "suffix": "" + }, + "unknown-80": { + "prefix": "", + "suffix": "" + }, + "unknown-100": { + "prefix": "", + "suffix": "" + } }, "battery_all": { - "charged": { "prefix": "", "suffix": "" }, - "AC": { "suffix": "" }, - "charging": { - "prefix": ["", "", "", "", ""], + "charged": { + "prefix": "", "suffix": "" }, - "discharging-10": { "prefix": "", "suffix": "" }, - "discharging-25": { "prefix": "", "suffix": "" }, - "discharging-50": { "prefix": "", "suffix": "" }, - "discharging-80": { "prefix": "", "suffix": "" }, - "discharging-100": { "prefix": "", "suffix": "" }, - "unlimited": { "prefix": "", "suffix": "" }, - "estimate": { "prefix": "" }, - "unknown-10": { "prefix": "", "suffix": "" }, - "unknown-25": { "prefix": "", "suffix": "" }, - "unknown-50": { "prefix": "", "suffix": "" }, - "unknown-80": { "prefix": "", "suffix": "" }, - "unknown-100": { "prefix": "", "suffix": "" } + "AC": { + "suffix": "" + }, + "charging": { + "prefix": [ + "", + "", + "", + "", + "" + ], + "suffix": "" + }, + "discharging-10": { + "prefix": "", + "suffix": "" + }, + "discharging-25": { + "prefix": "", + "suffix": "" + }, + "discharging-50": { + "prefix": "", + "suffix": "" + }, + "discharging-80": { + "prefix": "", + "suffix": "" + }, + "discharging-100": { + "prefix": "", + "suffix": "" + }, + "unlimited": { + "prefix": "", + "suffix": "" + }, + "estimate": { + "prefix": "" + }, + "unknown-10": { + "prefix": "", + "suffix": "" + }, + "unknown-25": { + "prefix": "", + "suffix": "" + }, + "unknown-50": { + "prefix": "", + "suffix": "" + }, + "unknown-80": { + "prefix": "", + "suffix": "" + }, + "unknown-100": { + "prefix": "", + "suffix": "" + } }, "caffeine": { - "activated": { "prefix": " " }, - "deactivated": { "prefix": " " } + "activated": { + "prefix": " " + }, + "deactivated": { + "prefix": " " + } }, "xrandr": { - "on": { "prefix": " " }, - "off": { "prefix": " " }, - "refresh": { "prefix": "" } + "on": { + "prefix": " " + }, + "off": { + "prefix": " " + }, + "refresh": { + "prefix": "" + } }, "redshift": { - "day": { "prefix": "" }, - "night": { "prefix": "" }, - "transition": { "prefix": "" } + "day": { + "prefix": "" + }, + "night": { + "prefix": "" + }, + "transition": { + "prefix": "" + } }, "docker_ps": { "prefix": "" @@ -191,32 +493,67 @@ "prefix": "" }, "sensors2": { - "temp": { "prefix": "" }, - "fan": { "prefix": "" }, - "cpu": { "prefix": "" } + "temp": { + "prefix": "" + }, + "fan": { + "prefix": "" + }, + "cpu": { + "prefix": "" + } }, "traffic": { - "rx": { "prefix": "" }, - "tx": { "prefix": "" } + "rx": { + "prefix": "" + }, + "tx": { + "prefix": "" + } }, "network_traffic": { - "rx": { "prefix": "" }, - "tx": { "prefix": "" } + "rx": { + "prefix": "" + }, + "tx": { + "prefix": "" + } }, "mpd": { - "playing": { "prefix": "" }, - "paused": { "prefix": "" }, - "stopped": { "prefix": "" }, - "prev": { "prefix": "" }, - "next": { "prefix": "" }, - "shuffle-on": { "prefix": "" }, - "shuffle-off": { "prefix": "" }, - "repeat-on": { "prefix": "" }, - "repeat-off": { "prefix": "" } + "playing": { + "prefix": "" + }, + "paused": { + "prefix": "" + }, + "stopped": { + "prefix": "" + }, + "prev": { + "prefix": "" + }, + "next": { + "prefix": "" + }, + "shuffle-on": { + "prefix": "" + }, + "shuffle-off": { + "prefix": "" + }, + "repeat-on": { + "prefix": "" + }, + "repeat-off": { + "prefix": "" + } }, "arch-update": { "prefix": " " }, + "aur-update": { + "prefix": " " + }, "github": { "prefix": "  " }, @@ -224,21 +561,41 @@ "prefix": "  " }, "spotify": { - "song": { "prefix": "" }, - "playing": { "prefix": "" }, - "paused": { "prefix": "" }, - "prev": { "prefix": "" }, - "next": { "prefix": "" } + "song": { + "prefix": "" + }, + "playing": { + "prefix": "" + }, + "paused": { + "prefix": "" + }, + "prev": { + "prefix": "" + }, + "next": { + "prefix": "" + } }, "publicip": { "prefix": "  " }, "weather": { - "clouds": { "prefix": "" }, - "rain": { "prefix": "" }, - "snow": { "prefix": "" }, - "clear": { "prefix": "" }, - "thunder": { "prefix": "" } + "clouds": { + "prefix": "" + }, + "rain": { + "prefix": "" + }, + "snow": { + "prefix": "" + }, + "clear": { + "prefix": "" + }, + "thunder": { + "prefix": "" + } }, "taskwarrior": { "prefix": "  " @@ -249,30 +606,56 @@ } }, "git": { - "main": { "prefix": "" }, - "new": { "prefix": "" }, - "modified": { "prefix": "" }, - "deleted": { "prefix": "" } + "main": { + "prefix": "" + }, + "new": { + "prefix": "" + }, + "modified": { + "prefix": "" + }, + "deleted": { + "prefix": "" + } }, "dunst": { - "muted": { "prefix": "" }, - "unmuted": { "prefix": "" } + "muted": { + "prefix": "" + }, + "unmuted": { + "prefix": "" + } }, "dunstctl": { - "muted": { "prefix": "" }, - "unmuted": { "prefix": "" }, - "unknown": { "prefix": "" } + "muted": { + "prefix": "" + }, + "unmuted": { + "prefix": "" + }, + "unknown": { + "prefix": "" + } }, "rofication": { - "prefix": "" + "prefix": "" }, "twmn": { - "muted": { "prefix": "" }, - "unmuted": { "prefix": "" } + "muted": { + "prefix": "" + }, + "unmuted": { + "prefix": "" + } }, "pihole": { - "enabled": { "prefix": "" }, - "disabled": { "prefix": "" } + "enabled": { + "prefix": "" + }, + "disabled": { + "prefix": "" + } }, "vpn": { "prefix": "" @@ -287,12 +670,22 @@ "prefix": "" }, "pomodoro": { - "off": { "prefix": "" }, - "paused": { "prefix": "" }, - "work": { "prefix": "" }, - "break": { "prefix": "" } + "off": { + "prefix": "" + }, + "paused": { + "prefix": "" + }, + "work": { + "prefix": "" + }, + "break": { + "prefix": "" + } + }, + "hddtemp": { + "prefix": "" }, - "hddtemp": { "prefix": "" }, "octoprint": { "prefix": " " }, @@ -300,10 +693,18 @@ "prefix": "" }, "speedtest": { - "running": { "prefix": ["\uf251", "\uf252", "\uf253"] }, - "not-running": { "prefix": "\uf144" } + "running": { + "prefix": [ + "\uf251", + "\uf252", + "\uf253" + ] + }, + "not-running": { + "prefix": "\uf144" + } }, "thunderbird": { "prefix": "" } -} +} \ No newline at end of file From 9b61eee725c794ad4dd1e7319ae1b3450afb7c08 Mon Sep 17 00:00:00 2001 From: ishaan Date: Tue, 15 Mar 2022 17:32:45 +0530 Subject: [PATCH 101/301] add better screenshots --- screenshots/arch-update.png | Bin 4952 -> 2409 bytes screenshots/aur-update.png | Bin 4718 -> 2214 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/screenshots/arch-update.png b/screenshots/arch-update.png index 42b268ee2c5a5fd373576bb71cac277a54b345e7..45613fcb330904d27e1c209f2473362a4247faa2 100644 GIT binary patch literal 2409 zcmV-v36}PWP)t#{L00009a7bBm000ie z000ie0hKEb8vpZ*+1Z`eot<{pY4_3DS?qR3t9807wJK;8Q2|BX@iqWi@N0JuG#s-{+xb&8^s z;jmd6bxLHI%x1T@bU$k9?!$4M>a@g99+XDYtVkAcIaC7xnw*{I8{jy8x3ztEe1haa zMJg7(zh_5kY_v=)TqX@kQ^tmch#R|lQ5x;#21jg!{Db`^6*cu#!%vu#M-S+R4UZ>H zRKtHC1VL7&B!&fxsRjU`Y$Ekty#t0ZvND^uHx&l){NhU$AM>kyB_AI<`1`~A)8Z9m z(^G-5Sf@@L5(n{#p+ppP>ck<8#q#%hUV?)JzkPQvRedHt*jIA=?HyG0Wq=?^YHalT zd*1l)m+w$*bC4Dn6D|p%ssI2W;BjWcqOD)oJupbJ20$tnMutksN=LsQ0765=2!ilA z*jvSgd=5r-_^A;h6i}sSLM9e**(`AoUn255+C4QdBE=SFr>)P;XzA&v8s;D|YI$t< zGOGFnjXKU-TlD=f+}zveZw-JVTuvTNP1`>`ck!bmKj*N$-)RONm5PPtDI3}C`8j#? zKv`u?ZfXLPL7y~R&tADPW;6kSR4m+CSP&5^owhq_THAg8iU|28O^z&Bhyc>7A39rp z(>!I1kVy_~+bH02P!#?6*g*hj>UvaKRt^A624kx>SFKba2=wTN&Q(+{_z39Wmfns^s7xzQty*sCiIX z-%MO06^pj6S}7F?N5?16T)m+mG5Ds~yLlZ-qy7EgSA5D`e1ET@qUJ%AT(&x61=Ti- z=&;b#m}sg>A|)=u8x{kj3+x>Tg6t^NXw)fWktil8C51X(F*;#1&s(%U14%KFfBfgy zZjWbuZpQxCHvHj>^8oP9Ya0hf41fKsl*ynU-nqr+Y_wP}Rn_zk4%1O|-{zuC1vzJ~ z+|Z90e)E?vQsZ9PQCRTn&q{sH0I++*YLS5VVd+=aDcg=B?OVl#e?DJEc9>0ig5qp> zB}$`hUz6_(i<76%0l=vfhjtZd|8(|WMvIk!q5zPSlCWWAW@%Y@uYQQn<ocb|N9 z^5+*Wjhihc>$JN!tp4cqIiE>4VxS?2Z#WNbj|Tu~NPu(YY{I31_YNPDAinoTxycD) zzIRg(h&o+1!R^xWN?>+4F1&200_4uAQ_<`!%=b@&#jhL>gp(R2l2^R1} zgaUbp_+sTB(aU}LFCgJE3C3de=!d#=gFFr<51|%rH|m-u%@!QT9WECDWG2SlZftGu(>YwO z$41i#si?22Zy7V1JRVQ&!_F{?-#Wj)*Kn=&DJtk67eY|o+CFYJQ&oSw*@BLu1?rS* z_Zuv>z{yjs^s6PEE>~G)^}W^(04V*oVr}A{_=TCOZ*JZtU$QVb4gfUp zJ{&?k<&$W$Ih@`Pb_b5*0v;Ct+#Zk7;!R7BjWdrb4-v1=%an_58bLJI}D9ICJzN%&e$Bsjj<Kg6N0F5jn8Tw!{7)RdR z_Q{!V0KhOl`QGkrzL+3bwos;;&9u|$_IQK>K2bLaxLmi}J?->HgNV;1J_xuNf*?kV z6#+nLG!C0Z{Qr2QQuh}-w_Lt+|MbOj500;hi&>wW?lS?7Q!?|o(S+mp$-?%bE zz~%6{oHd!Mwm!XYZ_=n!cpQw5q8nCb>V`+hP3FfYvwql6oS%)NC=bI{rKb{}G#ZVE zvB!-j96%xpTAQVsnFClnO4!CW~AQ%Swu;hydVnyTAPIy2Un4 z(HEpgKa})Jyu)lDr)L+95+wdUSFI4YF<`Un5?F?BQi{Sr|BUvg~>97qO{EgIUDn{ z5~CxkTiOAjqfaLb7HwOdSFFj-OHBlTwnw^|W%;uq--!@J%R_Ar2c?r?v)jMERz+6+mmn`^7he4I4Gm*VCZ&@x zXX%?5wOp~xuX(k^Vx_HL*E2XoR^|)~0LW0rQl*!~iBCT7*88n{i5E*lymUsx>h bf6IRWu5)CYO)!6P00000NkvXXu0mjfSYUuV literal 4952 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 literal 4718 zcmV-!5|QnRP)y0*A)h4t`NJtzcVk?16I#F+FR^)9uv!=}; zv`R=w93&E};bbBzFg7J}TCLR^EJCYkb{{G&9!G4k; z>g}ts_|(&9%gf@Q?EfbtBf}#j!?|=VR=#!9WK(Uw)2j2q62^|2iwaDdih1Kps z(OtE^&@^@N;(4uBq++MjYcE_lJ3Bk~aN;q`u|oHILZwoD{q=tWz}26x2ZBL?*Jia| z`s^YAeDlqB>2zA)mDrYnf&QaM4+Frxdk9nsMI7CIhdoTRcrKPuzA5RqulG=9#P0>Qr zdj-c8CX0K|7^fE`R_1zW_ic!)TgvIh|eIoit5<^2zyc|M9=? zGE^i<^2m`fx7&pv2*+`$R9d4^3kU559LI+IOXX5Y=-7y8bse$TTuLP=G`EP^Y_i*J zLTdwth6ZdlD~cijAeYN~db+;+@(Mw2$l)bgg{BXd$nADJiXzciESX56C~9wS_VQ>3 z4j`RQr_wllhhvEcse^hLQ^ZyGVaV+1GqRZZ?^PzWt<-FHAgqboJ`>YuB#FVyl>JS5>V>tb~2JY*T57FkIK^EozNU zVB8sIvq@;GBbi7(oP6wZJG;9&g}ycHb{j>J+3fcBs`ty+U;ja+qQCp@hfF4W=+H+_ zr=6mxN`;BVVzaYzUaw#H0ssJTINE!9x{U^d0#_6Y1)nc4_2enbibs_gChKrJU2d0N zuf=g30LkZbp>Sk&W-hk6T0he5cJ=l5(iBNjB#NK_U^E#1`q#hIkGXy8?!w|yy#)X; z40U(7yShAWY7MT$^Z7zJ9GRJZA$b2CgJCG2paz2>6vcXbd+c@_ilF-!NV6Q9PG_{* zHiE!~zBTj)-G%e#Pz1U0>#dc@iqO4NUe%QwFmzXoQ=+WKLaAi4-^&J(t2KJ9e$VcC z6tz`0{_^#_&*zV=u5KZ60MO~Q2VdkoI-Ty^*)y6p4gV*GVK$r9X0y)B%szbhsD31Z zAjgj%>Fw>QSxA!X>FILV+h5Nw)D5hHBIuv~^c6*INT6t%?xN{VPsiOm_vRNCYxcG_ zjb+Q*t0*K%UbuMHpx4(d6h(SGZjZ-(@BYNgxmR^#cZk($mC0lbQwfD42!b#St57H` zR&yXAETQiqxIzv9m2$(nRCiY=e`32kJ$oqfdJse;UfvC2QyYjSdbD)P~hyId*y3XEf^PUcL$i!?|2e zp->nM`jOFLgI<5)g)Oe9007OpB$oDMqx1Oj1(VE`Z;4%=*2 zhr_XdB3IxFf+PVTkxUAGYm`dW%3t}tuCz5zHk*uweM0M&R7I{ND3i^#CsBpbU^sVv zTrlnIn9AN&U-uiiTz>7^4ZlCY|GByvzj^Zxjw`KJ^T_CMP2>oI3=a8ERa z(wNUxW@cttmN|CpX!Y$j@^E6ZKCCSi3O=7d9FF|?>p$R1+~IQ6CR>(eYqw60WdVQ# zZtJz;;o*xFJ=Ki ztyb%FTHdJBYx$!0^XD%F+I#tOzE~{QTWW|VuH|w$5DEc+M!oX_8-gHRU7Y|hH#b*P zc#WserUAfcGVby_H5d&9K>$FlLct$U6sb4tO$`VE&}h^ejasMIx?GO)=g;=_^#H*0 z=hNwQMmTznVxf5V&b>RgfBPW1Gh)#o+n}v&3#v1DIJ9hcbYU|2MsI6i7X(&+gp5TOaWrKq6upT_h6W4|F=+>S`PSblU1Q9SCf=BLD!)vXMyC;b<4w zx1d-k0)T86Uz}Fg#tZoE8wP5=;%#ctiaYcS}>$4}SWYoI7{di-QEncAyL?|vZK zx>GEc7^Z?@GJ>c+PKQ5WSyogtnT&X=EP^0Tr`_G*Ht6*Pp%mOncN2tBWVJqe<@3)r zdNzP78y2XWfnPj}#}oCny?8vX)~K9L$CD>d*N5&Mzy5m5>uoK4J>baX3TwM3;Cs!N zw3=l7S}7I_q2L>9d&iDb2)ElMG;PG|_viEZ2M;FdyYQm1*wvq}mrA8vF30ym)xBaJ z6hRMvJbLx&b)jSLSqq0mKghaK7&UPX;wXk;F%0DL>SW97vr)abRtKs_E|*`paL#Hq z0|3ji=}e|nD)A*rm5LT@`)L&=UR7_S&(E`)Y)-9F@hx9`xnAJaYTHyQx-O#L;rTC> z%m4jfe**vnLB9C>vcaeyJ~ZU>`M3VRJ~(h(X|cM4{-x&suMHDIK9`P!y=IHE-q<`k zZ8hy|#F3B3qASsCHa9u>n7=eD3FkJb7v_OzGR6Yo#AMDiENYeV+70(_|MSg0 z<)u!?HdmCx>3A_S%OCIf{MX<(?!m*!&#zoE8VsGDj{5b*-v6Smd`M)iZwfw5P-EtP}Z;&wo8Dk(H&Tw;qrC z@W-QXm%V$~A%8%i)!GVEiLD6BI--coCyDq%4x~3W}iT&W~@a z@OK8sajUU70Px|l0KSbc7G2#OESbzkfdv3+eJQK4YBeFYc3?FN-ycO#lS%j{N`l}! z4#MGT4X+-fVLip&Or^pC0HNIK>AJDi7_R|$$2Qs4&LEUHzc}I*dvJhv*#`h-ld+cb z@A&)+c=YITrNR&dap=&H(D%EbO>1n^3a=JXL_`k!v*1#3p>;bV5#a)iC^Su9zWgb# za}-6-Ul^}#KRh7#0|5YVIP8L@JH0{YaJB=0*C$fWAqc|L;jXu!C^9hA4*n*6$Z-dhz=MEXyGX;&7~A13Nqa03=H(E8~5;& z1aW5k)S2;9%C*Yq2ZY{eqt)Bw$N+#DOyZGwE}K#kq+H%YO_gC7KCs*tg+k$Sxhj?N z*x?a-d$TJ>g?wRprrOK3uf*et-rjBu!(1IM6h%=KRjX7T9{0)P$7C{Du~@uy^A1zj zt2Z#vhvSM$g|R!@OQkX|EuB_-;^Z-_#R35L?>|f=)|;-1#iGmYBuUa}Hl&j2QmKUF zcz1W#(IX!-41-}KG?*_GZ)iNxm+&w_4ObKVm6ynsZ^<4#xP8+R%x|unaujqadai>bUO(`+1cq)C=^Vk!m%8R zp(>hoI2}ihj&*uE=EeGLn_+ZxSffz~0)hE?k=&SJ7>m`cqGEzL)$?yQn`|~K z!?J??UMnlH?ye36j;mDa#ZA5XJR_rrdV9NdS}n)1!JxTt#+FVbMhJ-$AyA#Z1!%v>7P>Y`TW6PSTxGo3`LPAPaYRqx8&U@8~_T1 z!Y{wvx^UsFLM|U2-SE?nrE>ZDzkex-<#3oowdv^>Z{EDEw=6Eb z(dl(VLjyLe)%NB3Lj_XF^whIw<7ZCQjRgRhn3(+Z(~A^EUc7j|-v0dgbfv-wK4Cra zc;dy(?7(2ZUaz}+`O}&`99gNqpiAZQ)t~=;?)F?i9~|tjxp*RCnL;zjWSGrn z0RXR0e3uRYc$fWFi>1BYB9mc!O>t-Ee=`hInVftwe)hE8ZnN9lePW+MuJIo`7uSADVKWV9asc2sE}Ko!w5ItV zKl29yZntwc(On}#;mG}a6WpeSlYJtc&MYmx0YDUTxlAUjFia|yT3A@Tefzh~jnMo| z9tZ^Q-Jf7tmLv$dTvn-6!r{n+2a|JRyE_7b;L6Ghj^j9vBM6$yW#?Zn+`4_YQmORy z_14ei0FcRKys(J`qnK2qKruF$`rGCX>!YBGK8|myf5OGIb^W=CRxD{Ep*$ zzddMF{VEg+0|R|1ibkWWwMV$Lbp9L7jt2?&0+q%0tDUzf}QlXFo0E(jgpA<=An5_Q$-6aT$P_!nKNj7={6teA47t0lR zyQ3RxTIaYDKYi+COKn@*^5)IkJ9qAF-GKcb;kTMjO+A~MdM5NqNW2rurJ~>an&12* zVEYiITq=hAi&jThlkE`2Lh<_bUyLSWhudv1>PdpwdiA>@9G5Q?W6{{c;*w}#Swcdh zhFpPL+C4$<{O0EN?L`g%g+eY8d}FqCG%wbb$jUzL&Xtgm5JjmZO;&d}uq05q_!8P? zvYAw?yI3V8B;GL;t={}ph-O6&tv`=KLPFx*v-$Z>Er=WdR%4-bvby(BLPFwTqSISc zn)O!5)M87CCYO+q w_^^;fE+HWyAtCW0gA5Fh32hP*62Bw<52K8BP;%z9+5i9m07*qoM6N<$g0ikSiU0rr From 82fa347f2c734fee654459c4eb75c604a9060e48 Mon Sep 17 00:00:00 2001 From: Bernhard B Date: Thu, 24 Mar 2022 21:19:52 +0100 Subject: [PATCH 102/301] extended pulseaudio module * added possibility to show currently selected default device in the statusbar (default: off) * allows to override the left mouse button click with a different action (e.g open popup menu to change the current default device) --- bumblebee_status/modules/core/pulseaudio.py | 69 ++++++++++++++++++--- 1 file changed, 62 insertions(+), 7 deletions(-) diff --git a/bumblebee_status/modules/core/pulseaudio.py b/bumblebee_status/modules/core/pulseaudio.py index 120470f..1213fe9 100644 --- a/bumblebee_status/modules/core/pulseaudio.py +++ b/bumblebee_status/modules/core/pulseaudio.py @@ -11,6 +11,20 @@ Parameters: Note: If the left and right channels have different volumes, the limit might not be reached exactly. * pulseaudio.showbars: 1 for showing volume bars, requires --markup=pango; 0 for not showing volume bars (default) + * pulseaudio.showdevicename: If set to 'true' (default is 'false'), the currently selected default device is shown. + Per default, the sink/source name returned by "pactl list sinks short" is used as display name. + + As this name is usually not particularly nice (e.g "alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo"), + its possible to map the name to more a user friendly name. + + e.g to map "alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo" to the name "Headset", add the following + bumblebee-status config entry: pulseaudio.alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo=Headset + + Furthermore its possible to specify individual (unicode) icons for all sinks/sources. e.g in order to use the icon 🎧 for the + "alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo" sink, add the following bumblebee-status config entry: + pulseaudio.icon.alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo=🎧 + * Per default a left mouse button click mutes/unmutes the device. In case you want to open a dropdown menu to change the current + default device add the following config entry to your bumblebee-status config: pulseaudio.left-click=select_default_device_popup Requires the following executable: * pulseaudio @@ -20,6 +34,7 @@ Requires the following executable: import re import logging +import functools import core.module import core.widget @@ -28,11 +43,12 @@ import core.input import util.cli import util.graph import util.format +import util.popup class Module(core.module.Module): def __init__(self, config, theme, channel): - super().__init__(config, theme, core.widget.Widget(self.volume)) + super().__init__(config, theme, core.widget.Widget(self.display)) if util.format.asbool(self.parameter("autostart", False)): util.cli.execute("pulseaudio --start", ignore_errors=True) @@ -48,7 +64,11 @@ class Module(core.module.Module): self._mute = False self._failed = False self._channel = channel + self.__selected_default_device = None self._showbars = util.format.asbool(self.parameter("showbars", 0)) + self.__show_device_name = util.format.asbool( + self.parameter("showdevicename", False) + ) self._patterns = [ {"expr": "Name:", "callback": (lambda line: False)}, @@ -138,19 +158,19 @@ class Module(core.module.Module): logging.error("no pulseaudio device found") return "n/a" - def volume(self, widget): + def display(self, widget): if self._failed == True: return "n/a" + + vol = None if int(self._mono) > 0: vol = "{}%".format(self._mono) if self._showbars: vol = "{} {}".format(vol, util.graph.hbar(float(self._mono))) - return vol elif self._left == self._right: vol = "{}%".format(self._left) if self._showbars: vol = "{} {}".format(vol, util.graph.hbar(float(self._left))) - return vol else: vol = "{}%/{}%".format(self._left, self._right) if self._showbars: @@ -159,19 +179,31 @@ class Module(core.module.Module): util.graph.hbar(float(self._left)), util.graph.hbar(float(self._right)), ) - return vol + + output = vol + if self.__show_device_name: + friendly_name = self.parameter( + self.__selected_default_device, self.__selected_default_device + ) + icon = self.parameter("icon." + self.__selected_default_device, "") + output = ( + icon + " " + friendly_name + " | " + vol + if icon != "" + else friendly_name + " | " + vol + ) + return output def update(self): try: self._failed = False channel = "sinks" if self._channel == "sink" else "sources" - device = self._default_device() + self.__selected_default_device = self._default_device() result = util.cli.execute("pactl list {}".format(channel)) found = False for line in result.split("\n"): - if "Name: {}".format(device) in line: + if "Name: {}".format(self.__selected_default_device) in line: found = True continue if found is False: @@ -189,6 +221,29 @@ class Module(core.module.Module): else: raise e + def __on_sink_selected(self, sink_name): + util.cli.execute("pactl set-default-{} {}".format(self._channel, sink_name)) + + def select_default_device_popup(self, widget): + channel = "sinks" if self._channel == "sink" else "sources" + result = util.cli.execute("pactl list {} short".format(channel)) + + menu = util.popup.menu() + lines = result.splitlines() + for line in lines: + info = line.split("\t") + try: + friendly_name = self.parameter(info[1], info[1]) + menu.add_menuitem( + friendly_name, + callback=functools.partial(self.__on_sink_selected, info[1]), + ) + except: + logging.exception("Couldn't parse {}".format(channel)) + pass + + menu.show(widget) + def state(self, widget): if self._mute: return ["warning", "muted"] From 4d3de3be0446877caa436bbc571b449fbb7fba71 Mon Sep 17 00:00:00 2001 From: Bernhard B Date: Fri, 25 Mar 2022 19:29:37 +0100 Subject: [PATCH 103/301] handle util.popup ImportError gracefully * util.popup requires tkinter to be installed (in order to display popups). As most people will probably use the default configuration of the pulseaudio module (where the popup is disabled) and in order to avoid breaking existing setups, we catch import errors and just log them. --- bumblebee_status/modules/core/pulseaudio.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bumblebee_status/modules/core/pulseaudio.py b/bumblebee_status/modules/core/pulseaudio.py index 1213fe9..906e1dd 100644 --- a/bumblebee_status/modules/core/pulseaudio.py +++ b/bumblebee_status/modules/core/pulseaudio.py @@ -43,8 +43,11 @@ import core.input import util.cli import util.graph import util.format -import util.popup +try: + import util.popup +except ImportError as e: + logging.warning("Couldn't import util.popup: %s. Popups won't work!", e) class Module(core.module.Module): def __init__(self, config, theme, channel): From 8501c406aff6a8f6a87f4c504e7a7f3bfab5a02e Mon Sep 17 00:00:00 2001 From: arivarton Date: Fri, 8 Apr 2022 14:15:28 +0200 Subject: [PATCH 104/301] Google calendar module. --- bumblebee_status/modules/contrib/gcalendar.py | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 bumblebee_status/modules/contrib/gcalendar.py diff --git a/bumblebee_status/modules/contrib/gcalendar.py b/bumblebee_status/modules/contrib/gcalendar.py new file mode 100644 index 0000000..e1ce219 --- /dev/null +++ b/bumblebee_status/modules/contrib/gcalendar.py @@ -0,0 +1,113 @@ +"""Displays first upcoming event in google calendar. + +Events that are set as 'all-day' will not be shown. + +Requires credentials.json from a google api application where the google calendar api is installed. +On first time run the browser will open and google will ask for permission for this app to access the google calendar and then save a .gcalendar_token.json file to the credentials_path directory which stores this permission. + +A refresh is done every 15 minutes. + +Parameters: + * gcalendar.time_format: Format time output. Defaults to "%H:%M". + * gcalendar.date_format: Format date output. Defaults to "%d.%m.%y". + * gcalendar.credentials_path: Path to credentials.json. Defaults to "~/". + +Requires these pip packages: + * google-api-python-client + * google-auth-httplib2 + * google-auth-oauthlib +""" + +# This import belongs to the google code +from __future__ import print_function + +from dateutil.parser import parse as dtparse + +import core.module +import core.widget +import core.decorators + +import datetime +import os.path + +from google.auth.transport.requests import Request +from google.oauth2.credentials import Credentials +from google_auth_oauthlib.flow import InstalledAppFlow +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + +class Module(core.module.Module): + @core.decorators.every(minutes=15) + def __init__(self, config, theme): + super().__init__(config, theme, core.widget.Widget(self.first_event)) + self.__time_format = self.parameter("time_format", "%H:%M") + self.__date_format = self.parameter("date_format", "%d.%m.%y") + self.__credentials_path = os.path.expanduser(self.parameter("credentials_path", "~/")) + self.__credentials = os.path.join(self.__credentials_path, 'credentials.json') + self.__token = os.path.join(self.__credentials_path, '.gcalendar_token.json') + + def first_event(self, widget): + SCOPES = ['https://www.googleapis.com/auth/calendar.readonly'] + + """Shows basic usage of the Google Calendar API. + Prints the start and name of the next 10 events on the user's calendar. + """ + creds = None + # The file token.json stores the user's access and refresh tokens, and is + # created automatically when the authorization flow completes for the first + # time. + if os.path.exists(self.__token): + creds = Credentials.from_authorized_user_file(self.__token, SCOPES) + # If there are no (valid) credentials available, let the user log in. + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + self.__credentials, SCOPES) + creds = flow.run_local_server(port=0) + # Save the credentials for the next run + with open(self.__token, 'w') as token: + token.write(creds.to_json()) + + try: + service = build('calendar', 'v3', credentials=creds) + + # Call the Calendar API + now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time + end = (datetime.datetime.utcnow() + datetime.timedelta(days=7)).isoformat() + 'Z' # 'Z' indicates UTC time + # Get all calendars + calendar_list = service.calendarList().list().execute() + event_list = [] + for calendar_list_entry in calendar_list['items']: + calendar_id = calendar_list_entry['id'] + events_result = service.events().list(calendarId=calendar_id, timeMin=now, + timeMax=end, + singleEvents=True, + orderBy='startTime').execute() + events = events_result.get('items', []) + + if not events: + return 'No upcoming events found.' + + for event in events: + start = dtparse(event['start'].get('dateTime', event['start'].get('date'))) + # Only add to list if not an whole day event + if start.tzinfo: + event_list.append({'date': start, + 'summary': event['summary'], + 'type': event['eventType']}) + sorted_list = sorted(event_list, key=lambda t: t['date']) + + for gevent in sorted_list: + if gevent['date'] >= datetime.datetime.now(datetime.timezone.utc): + if gevent['date'].date() == datetime.datetime.utcnow().date(): + return str('%s %s' % (gevent['date'].astimezone().strftime(f'{self.__time_format}'), gevent['summary'])) + else: + return str('%s %s' % (gevent['date'].astimezone().strftime(f'{self.__date_format} {__time_format}'), gevent['summary'])) + return 'No upcoming events found.' + + except: + return None + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From a48ddbb2c80a42e79ecd78c91519239c48c84645 Mon Sep 17 00:00:00 2001 From: arivarton Date: Fri, 8 Apr 2022 14:23:22 +0200 Subject: [PATCH 105/301] Ran black -t py34 --- bumblebee_status/modules/contrib/gcalendar.py | 90 +++++++++++++------ 1 file changed, 63 insertions(+), 27 deletions(-) diff --git a/bumblebee_status/modules/contrib/gcalendar.py b/bumblebee_status/modules/contrib/gcalendar.py index e1ce219..418f059 100644 --- a/bumblebee_status/modules/contrib/gcalendar.py +++ b/bumblebee_status/modules/contrib/gcalendar.py @@ -36,18 +36,21 @@ from google_auth_oauthlib.flow import InstalledAppFlow from googleapiclient.discovery import build from googleapiclient.errors import HttpError + class Module(core.module.Module): @core.decorators.every(minutes=15) def __init__(self, config, theme): super().__init__(config, theme, core.widget.Widget(self.first_event)) self.__time_format = self.parameter("time_format", "%H:%M") self.__date_format = self.parameter("date_format", "%d.%m.%y") - self.__credentials_path = os.path.expanduser(self.parameter("credentials_path", "~/")) - self.__credentials = os.path.join(self.__credentials_path, 'credentials.json') - self.__token = os.path.join(self.__credentials_path, '.gcalendar_token.json') + self.__credentials_path = os.path.expanduser( + self.parameter("credentials_path", "~/") + ) + self.__credentials = os.path.join(self.__credentials_path, "credentials.json") + self.__token = os.path.join(self.__credentials_path, ".gcalendar_token.json") def first_event(self, widget): - SCOPES = ['https://www.googleapis.com/auth/calendar.readonly'] + SCOPES = ["https://www.googleapis.com/auth/calendar.readonly"] """Shows basic usage of the Google Calendar API. Prints the start and name of the next 10 events on the user's calendar. @@ -64,50 +67,83 @@ class Module(core.module.Module): creds.refresh(Request()) else: flow = InstalledAppFlow.from_client_secrets_file( - self.__credentials, SCOPES) + self.__credentials, SCOPES + ) creds = flow.run_local_server(port=0) # Save the credentials for the next run - with open(self.__token, 'w') as token: + with open(self.__token, "w") as token: token.write(creds.to_json()) try: - service = build('calendar', 'v3', credentials=creds) + service = build("calendar", "v3", credentials=creds) # Call the Calendar API - now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time - end = (datetime.datetime.utcnow() + datetime.timedelta(days=7)).isoformat() + 'Z' # 'Z' indicates UTC time + now = datetime.datetime.utcnow().isoformat() + "Z" # 'Z' indicates UTC time + end = ( + datetime.datetime.utcnow() + datetime.timedelta(days=7) + ).isoformat() + "Z" # 'Z' indicates UTC time # Get all calendars calendar_list = service.calendarList().list().execute() event_list = [] - for calendar_list_entry in calendar_list['items']: - calendar_id = calendar_list_entry['id'] - events_result = service.events().list(calendarId=calendar_id, timeMin=now, - timeMax=end, - singleEvents=True, - orderBy='startTime').execute() - events = events_result.get('items', []) + for calendar_list_entry in calendar_list["items"]: + calendar_id = calendar_list_entry["id"] + events_result = ( + service.events() + .list( + calendarId=calendar_id, + timeMin=now, + timeMax=end, + singleEvents=True, + orderBy="startTime", + ) + .execute() + ) + events = events_result.get("items", []) if not events: - return 'No upcoming events found.' + return "No upcoming events found." for event in events: - start = dtparse(event['start'].get('dateTime', event['start'].get('date'))) + start = dtparse( + event["start"].get("dateTime", event["start"].get("date")) + ) # Only add to list if not an whole day event if start.tzinfo: - event_list.append({'date': start, - 'summary': event['summary'], - 'type': event['eventType']}) - sorted_list = sorted(event_list, key=lambda t: t['date']) + event_list.append( + { + "date": start, + "summary": event["summary"], + "type": event["eventType"], + } + ) + sorted_list = sorted(event_list, key=lambda t: t["date"]) for gevent in sorted_list: - if gevent['date'] >= datetime.datetime.now(datetime.timezone.utc): - if gevent['date'].date() == datetime.datetime.utcnow().date(): - return str('%s %s' % (gevent['date'].astimezone().strftime(f'{self.__time_format}'), gevent['summary'])) + if gevent["date"] >= datetime.datetime.now(datetime.timezone.utc): + if gevent["date"].date() == datetime.datetime.utcnow().date(): + return str( + "%s %s" + % ( + gevent["date"] + .astimezone() + .strftime(f"{self.__time_format}"), + gevent["summary"], + ) + ) else: - return str('%s %s' % (gevent['date'].astimezone().strftime(f'{self.__date_format} {__time_format}'), gevent['summary'])) - return 'No upcoming events found.' + return str( + "%s %s" + % ( + gevent["date"] + .astimezone() + .strftime(f"{self.__date_format} {__time_format}"), + gevent["summary"], + ) + ) + return "No upcoming events found." except: return None + # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From 83d910a7ef097a7cdb8cac2cc19c50188b42769b Mon Sep 17 00:00:00 2001 From: arivarton Date: Fri, 8 Apr 2022 17:09:28 +0200 Subject: [PATCH 106/301] Changed __time_format to self.__time_format. --- bumblebee_status/modules/contrib/gcalendar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bumblebee_status/modules/contrib/gcalendar.py b/bumblebee_status/modules/contrib/gcalendar.py index 418f059..e438bdd 100644 --- a/bumblebee_status/modules/contrib/gcalendar.py +++ b/bumblebee_status/modules/contrib/gcalendar.py @@ -136,7 +136,7 @@ class Module(core.module.Module): % ( gevent["date"] .astimezone() - .strftime(f"{self.__date_format} {__time_format}"), + .strftime(f"{self.__date_format} {self.__time_format}"), gevent["summary"], ) ) From 771e7482d719994e72a0381d449ecc54cf342342 Mon Sep 17 00:00:00 2001 From: Samuel Tebbs Date: Sat, 23 Apr 2022 14:44:35 +0100 Subject: [PATCH 107/301] [modules/playerctl] add 'hide' parameter --- bumblebee_status/modules/contrib/playerctl.py | 15 ++++++++++++--- docs/modules.rst | 1 + 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/bumblebee_status/modules/contrib/playerctl.py b/bumblebee_status/modules/contrib/playerctl.py index 56af426..7fef412 100755 --- a/bumblebee_status/modules/contrib/playerctl.py +++ b/bumblebee_status/modules/contrib/playerctl.py @@ -12,6 +12,7 @@ Parameters: Widget names are: playerctl.song, playerctl.prev, playerctl.pause, playerctl.next * playerctl.args: The arguments added to playerctl. You can check 'playerctl --help' or `its README `_. For example, it could be '-p vlc,%any'. + * playerctl.hide: Hide the widgets when no players are found. Defaults to "false". Parameters are inspired by the `spotify` module, many thanks to its developers! @@ -32,6 +33,8 @@ class Module(core.module.Module): self.background = True + self.__hide = util.format.asbool(self.parameter("hide", "false")); + self.__layout = util.format.aslist( self.parameter( "layout", "playerctl.prev, playerctl.song, playerctl.pause, playerctl.next" @@ -83,14 +86,20 @@ class Module(core.module.Module): if isinstance(callback_options, dict): core.input.register(widget, **callback_options) - def update(self): + def hidden(self): + return self.__hide and self.status() == None + + def status(self): try: playback_status = str(util.cli.execute(self.__cmd + "status 2>&1 || true", shell = True)).strip() if playback_status == "No players found": - playback_status = None + return None + return playback_status except Exception as e: logging.exception(e) - playback_status = None + return None + + def update(self): for widget in self.widgets(): if playback_status: if widget.name == "playerctl.pause": diff --git a/docs/modules.rst b/docs/modules.rst index ad4a307..24046a0 100644 --- a/docs/modules.rst +++ b/docs/modules.rst @@ -1095,6 +1095,7 @@ Parameters: Widget names are: playerctl.song, playerctl.prev, playerctl.pause, playerctl.next * playerctl.args: The arguments added to playerctl. You can check 'playerctl --help' or `its readme `_. For example, it could be '-p vlc,%any'. + * playerctl.hide: Hide the widgets when no players are found. Defaults to "false". Parameters are inspired by the `spotify` module, many thanks to its developers! From d20dacb2dc9bca6f1d2fb8a79665f2175af7d7f5 Mon Sep 17 00:00:00 2001 From: Samuel Tebbs Date: Sun, 24 Apr 2022 16:09:54 +0100 Subject: [PATCH 108/301] Fix missing playback_status --- bumblebee_status/modules/contrib/playerctl.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bumblebee_status/modules/contrib/playerctl.py b/bumblebee_status/modules/contrib/playerctl.py index 7fef412..bd8876c 100755 --- a/bumblebee_status/modules/contrib/playerctl.py +++ b/bumblebee_status/modules/contrib/playerctl.py @@ -100,6 +100,7 @@ class Module(core.module.Module): return None def update(self): + playback_status = self.status() for widget in self.widgets(): if playback_status: if widget.name == "playerctl.pause": From 3da0f08fcb370d298cf0313540d76344c308ff56 Mon Sep 17 00:00:00 2001 From: alexcoder04 Date: Sun, 1 May 2022 12:15:29 +0200 Subject: [PATCH 109/301] arch-update: sleep 1 sec before checking When waking up from suspend, there is sometimes a delay connecting to the network, so arch-update gives an error --- bumblebee_status/modules/contrib/arch-update.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bumblebee_status/modules/contrib/arch-update.py b/bumblebee_status/modules/contrib/arch-update.py index ed9ae58..d8da8fb 100644 --- a/bumblebee_status/modules/contrib/arch-update.py +++ b/bumblebee_status/modules/contrib/arch-update.py @@ -7,6 +7,7 @@ contributed by `lucassouto `_ - many thanks! """ import logging +from time import sleep import core.module import core.widget @@ -35,6 +36,7 @@ class Module(core.module.Module): def update(self): self.__error = False + sleep(1) code, result = util.cli.execute( "checkupdates", ignore_errors=True, return_exitcode=True ) From a6d2ccc666ce84acad96dd8116cbb84ea1e2c1ca Mon Sep 17 00:00:00 2001 From: Timoses Date: Wed, 11 May 2022 19:58:34 +0200 Subject: [PATCH 110/301] Fix logout item using hardcoded command --- bumblebee_status/modules/contrib/system.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bumblebee_status/modules/contrib/system.py b/bumblebee_status/modules/contrib/system.py index cc46ef8..00e80e0 100644 --- a/bumblebee_status/modules/contrib/system.py +++ b/bumblebee_status/modules/contrib/system.py @@ -96,7 +96,7 @@ class Module(core.module.Module): menu.add_menuitem( "log out", callback=functools.partial( - self.__on_command, "Log out", "Log out?", "i3exit logout" + self.__on_command, "Log out", "Log out?", logout_cmd ), ) # don't ask for these From f01179290b7a62548c3439c103616e2c5c744253 Mon Sep 17 00:00:00 2001 From: Sam Tebbs Date: Fri, 13 May 2022 10:21:31 +0100 Subject: [PATCH 111/301] Hide progress module if it's inactive --- bumblebee_status/modules/contrib/progress.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bumblebee_status/modules/contrib/progress.py b/bumblebee_status/modules/contrib/progress.py index 7e148b3..9148acb 100644 --- a/bumblebee_status/modules/contrib/progress.py +++ b/bumblebee_status/modules/contrib/progress.py @@ -29,6 +29,9 @@ class Module(core.module.Module): super().__init__(config, theme, core.widget.Widget(self.get_progress_text)) self.__active = False + def hidden(self): + return not self.__active + def get_progress_text(self, widget): if self.update_progress_info(widget): width = util.format.asint(self.parameter("barwidth", 8)) From 91b1b5e037b761453d55de0383e90efbe6e1505f Mon Sep 17 00:00:00 2001 From: Sam Tebbs Date: Fri, 20 May 2022 09:21:20 +0100 Subject: [PATCH 112/301] Add popup command parameter to the system module --- bumblebee_status/modules/contrib/system.py | 5 +++++ docs/modules.rst | 1 + 2 files changed, 6 insertions(+) diff --git a/bumblebee_status/modules/contrib/system.py b/bumblebee_status/modules/contrib/system.py index 00e80e0..79e8846 100644 --- a/bumblebee_status/modules/contrib/system.py +++ b/bumblebee_status/modules/contrib/system.py @@ -72,6 +72,11 @@ class Module(core.module.Module): util.cli.execute(command) def popup(self, widget): + popupcmd = self.parameter("popupcmd", ""); + if (popupcmd != ""): + util.cli.execute(popupcmd) + return + menu = util.popup.menu() reboot_cmd = self.parameter("reboot", "reboot") shutdown_cmd = self.parameter("shutdown", "shutdown -h now") diff --git a/docs/modules.rst b/docs/modules.rst index 24046a0..69e56fd 100644 --- a/docs/modules.rst +++ b/docs/modules.rst @@ -1413,6 +1413,7 @@ Parameters: * system.lock: specify a command for locking the screen (defaults to 'i3exit lock') * system.suspend: specify a command for suspending (defaults to 'i3exit suspend') * system.hibernate: specify a command for hibernating (defaults to 'i3exit hibernate') + * system.popupcmd: specify a command to run instead of opening the default menu Requirements: tkinter (python3-tk package on debian based systems either you can install it as python package) From 5c166beebfd4b4a7fb8053e2ddb142801368d9cc Mon Sep 17 00:00:00 2001 From: FraSharp Date: Thu, 26 May 2022 13:00:40 +0200 Subject: [PATCH 113/301] [modules]: introduce pamixer module Signed-off-by: FraSharp --- bumblebee_status/modules/contrib/pamixer.py | 88 +++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 bumblebee_status/modules/contrib/pamixer.py diff --git a/bumblebee_status/modules/contrib/pamixer.py b/bumblebee_status/modules/contrib/pamixer.py new file mode 100644 index 0000000..2396949 --- /dev/null +++ b/bumblebee_status/modules/contrib/pamixer.py @@ -0,0 +1,88 @@ +"""get volume level or control it + +Requires the following executable: + * pamixer + +Parameters: + * pamixer.percent_change: How much to change volume by when scrolling on the module (default is 4%) + +heavily based on amixer module +""" +import re + +import core.module +import core.widget +import core.input + +import util.cli +import util.format + +class Module(core.module.Module): + def __init__(self, config, theme): + super().__init__(config, theme, core.widget.Widget(self.volume)) + + self.__level = "volume 0%" + self.__muted = True + self.__change = util.format.asint( + self.parameter("percent_change", "4%").strip("%"), 0, 200 + ) + + events = [ + { + "type": "mute", + "action": self.toggle, + "button": core.input.LEFT_MOUSE, + }, + { + "type": "volume", + "action": self.increase_volume, + "button": core.input.WHEEL_UP, + }, + { + "type": "volume", + "action": self.decrease_volume, + "button": core.input.WHEEL_DOWN, + }, + ] + + for event in events: + core.input.register(self, button=event["button"], cmd=event["action"]) + + def toggle(self, event): + self.set_parameter("--set-level 0") + + def increase_volume(self, event): + self.set_parameter("--increase {}".format(self.__change)) + + def decrease_volume(self, event): + self.set_parameter("--decrease {}".format(self.__change)) + + def set_parameter(self, parameter): + util.cli.execute("pamixer {}".format(parameter)) + + def volume(self, widget): + if self.__level == "volume 0%": + self.__muted = True + return self.__level + m = re.search(r"([\d]+)\%", self.__level) + if m: + if m.group(1) != "0%" in self.__level: + self.__muted = False + return "volume {}%".format(m.group(1)) + else: + return "volume 0%" + + def update(self): + try: + volume = util.cli.execute("pamixer --get-volume-human".format()) + self.__level = volume + self.__muted = False + except Exception as e: + self.__level = "volume 0%" + + def state(self, widget): + if self.__muted: + return ["warning", "muted"] + return ["unmuted"] + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From c001f031a15170069a8fc370d27d50f2012c4397 Mon Sep 17 00:00:00 2001 From: benthetechguy Date: Tue, 7 Jun 2022 19:14:51 -0400 Subject: [PATCH 114/301] Change nonexistent image to link --- docs/FAQ.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/FAQ.rst b/docs/FAQ.rst index eeaff82..8c927cf 100644 --- a/docs/FAQ.rst +++ b/docs/FAQ.rst @@ -29,9 +29,9 @@ didn’t have background color support for the status bar. Some of the icons don’t render correctly ---------------------------------------- -Please check that you have |Font Awesome| installed (version 4). +Please check that you have `Font Awesome`_ installed (version 4). -.. note:: The |Font Awesome| is required for all themes that +.. note:: The `Font Awesome`_ is required for all themes that contain icons (because that is the font that includes these icons). Please refer to your distribution’s package management on how to install them, or get them from their website directly. Also, please note that @@ -52,4 +52,4 @@ Please check that you have |Font Awesome| installed (version 4). # Other # see https://github.com/gabrielelana/awesome-terminal-fonts -.. |Font Awesome| image:: https://fontawesome.com/ +.. _Font Awesome: https://fontawesome.com/ From 4afb8d863674509df5a42020b5e782c3f2e27017 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Wed, 8 Jun 2022 08:32:35 +0200 Subject: [PATCH 115/301] [doc] add man pages Many thanks to https://github.com/benthetechguy, who wrote man pages for bumblebee-status and bumblebee-ctl fixes #882 --- man/bumblebee-ctl.1 | 29 +++++++++++++++++++++ man/bumblebee-status.1 | 58 ++++++++++++++++++++++++++++++++++++++++++ setup.py | 1 + 3 files changed, 88 insertions(+) create mode 100644 man/bumblebee-ctl.1 create mode 100644 man/bumblebee-status.1 diff --git a/man/bumblebee-ctl.1 b/man/bumblebee-ctl.1 new file mode 100644 index 0000000..33c50aa --- /dev/null +++ b/man/bumblebee-ctl.1 @@ -0,0 +1,29 @@ +.TH BUMBLEBEE-CTL "1" "June 2022" "bumblebee-status" +.SH NAME +bumblebee-ctl \- Send commands to bumblebee-status +.SH SYNOPSIS +.B bumblebee-ctl +[\fB\-h\fR] [\fB\-b\fR \fIbutton\fR] [\fB\-i\fR \fIID\fR] \fB-m\fR \fImodule\fR +.SH DESCRIPTION +.B bumblebee-ctl +can be used to send commands to bumblebee-status. +.SH OPTIONS +.TP +\fB\-h\fR, \fB\-\-help\fR +show this help message and exit +.TP +\fB\-b\fR, \fB\-\-button\fR