From fded39fa810ee95565e6e90dcd5da3512f3cebb9 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Mon, 11 Sep 2023 09:36:47 +0200 Subject: [PATCH 01/10] [modules/pulsectl] make device names case sensitive A previous change accidentially changed the "pretty" device name mapping to be required to be in lowercase (rather than the exact name of the devices. Restore previous functionality. fixes #989 --- bumblebee_status/modules/core/pulsectl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bumblebee_status/modules/core/pulsectl.py b/bumblebee_status/modules/core/pulsectl.py index 701f6ef..fd77c26 100644 --- a/bumblebee_status/modules/core/pulsectl.py +++ b/bumblebee_status/modules/core/pulsectl.py @@ -110,8 +110,8 @@ class Module(core.module.Module): res = f"{res} {util.graph.hbar(self.__volume*100)}" if self.__show_device_name: - friendly_name = self.parameter(self.__devicename.lower(), self.__devicename) - icon = self.parameter("icon." + self.__devicename.lower(), "") + friendly_name = self.parameter(self.__devicename, self.__devicename) + icon = self.parameter("icon." + self.__devicename, "") res = ( icon + " " + friendly_name + " | " + res if icon != "" From b76213203792107bde548f497a9b13407cd0e532 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20L=C3=BCftinger?= Date: Wed, 13 Sep 2023 18:02:26 +0200 Subject: [PATCH 02/10] poll window title also on workspace change (not only window events) --- bumblebee_status/modules/contrib/title.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bumblebee_status/modules/contrib/title.py b/bumblebee_status/modules/contrib/title.py index 055369e..1c98b42 100644 --- a/bumblebee_status/modules/contrib/title.py +++ b/bumblebee_status/modules/contrib/title.py @@ -50,8 +50,9 @@ class Module(core.module.Module): # create a connection with i3ipc self.__i3 = i3ipc.Connection() - # event is called both on focus change and title change + # event is called both on focus change and title change, and on workspace change self.__i3.on("window", lambda __p_i3, __p_e: self.__pollTitle()) + self.__i3.on("workspace", lambda __p_i3, __p_e: self.__pollTitle()) # begin listening for events threading.Thread(target=self.__i3.main).start() From b42323013d5fdb6dc47b487d2d863e0556fd85a8 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Fri, 15 Sep 2023 15:06:57 +0200 Subject: [PATCH 03/10] fix(config): make config file keys case sensitive Since the configparser library by default parses keys case insensitive (all lowercase), certain mappings, especially in the pulseaudio modules, could fail ("internal" pulseaudio device names are matched against entries in the configuration). fixes #992 --- bumblebee_status/core/config.py | 11 ++++++++--- tests/core/test_config.py | 6 ++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/bumblebee_status/core/config.py b/bumblebee_status/core/config.py index 1d4ebaa..0b564b3 100644 --- a/bumblebee_status/core/config.py +++ b/bumblebee_status/core/config.py @@ -240,11 +240,16 @@ class Config(util.store.Store): :param filename: path to the file to load """ - def load_config(self, filename): - if os.path.exists(filename): + def load_config(self, filename, content=None): + if os.path.exists(filename) or content != None: log.info("loading {}".format(filename)) tmp = RawConfigParser() - tmp.read(u"{}".format(filename)) + tmp.optionxform = str + + if content: + tmp.read_string(content) + else: + tmp.read(u"{}".format(filename)) if tmp.has_section("module-parameters"): for key, value in tmp.items("module-parameters"): diff --git a/tests/core/test_config.py b/tests/core/test_config.py index 762c674..02695cd 100644 --- a/tests/core/test_config.py +++ b/tests/core/test_config.py @@ -113,6 +113,12 @@ def test_missing_parameter(): assert cfg.get("test.key") == None assert cfg.get("test.key", "no-value-set") == "no-value-set" +def test_file_case_sensitivity(): + cfg = core.config.Config([]) + cfg.load_config("", content="[module-parameters]\ntest.key = VaLuE\ntest.KeY2 = value") + + assert cfg.get("test.key") == "VaLuE" + assert cfg.get("test.KeY2") == "value" # # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From d303b794f3147b01662d889a4b2e718256e799af Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Fri, 15 Sep 2023 15:11:23 +0200 Subject: [PATCH 04/10] feat(docs): clarify line continuation fixes #987 --- docs/introduction.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/introduction.rst b/docs/introduction.rst index 3831d4b..891b14c 100644 --- a/docs/introduction.rst +++ b/docs/introduction.rst @@ -44,6 +44,14 @@ like this: -t } +Line continuations (breaking a single line into multiple lines) is allowed in +the i3 configuration, but please ensure that all lines except the final one need to have a trailing +"\". +This is explained in detail here: +[i3 user guide: line continuation](https://i3wm.org/docs/userguide.html#line_continuation) + + + You can retrieve a list of modules (and their parameters) and themes by entering: From 2e7e75a27c6a9dac00f401540fa337417820df49 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Fri, 15 Sep 2023 15:15:31 +0200 Subject: [PATCH 05/10] feat(shell): add timeout warning and faster update see #990 --- bumblebee_status/modules/contrib/shell.py | 2 ++ bumblebee_status/util/cli.py | 14 +++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/bumblebee_status/modules/contrib/shell.py b/bumblebee_status/modules/contrib/shell.py index 566de42..7043778 100644 --- a/bumblebee_status/modules/contrib/shell.py +++ b/bumblebee_status/modules/contrib/shell.py @@ -61,6 +61,7 @@ class Module(core.module.Module): # if requested then run not async version and just execute command in this thread if not self.__async: self.__output = util.cli.execute(self.__command, shell=True, ignore_errors=True).strip() + core.event.trigger("update", [self.id], redraw_only=True) return # if previous thread didn't end yet then don't do anything @@ -71,6 +72,7 @@ class Module(core.module.Module): self.__current_thread = threading.Thread( target=lambda obj, cmd: obj.set_output( util.cli.execute(cmd, ignore_errors=True) + core.event.trigger("update", [self.id], redraw_only=True) ), args=(self, self.__command), ) diff --git a/bumblebee_status/util/cli.py b/bumblebee_status/util/cli.py index d14dc6a..4ef25d9 100644 --- a/bumblebee_status/util/cli.py +++ b/bumblebee_status/util/cli.py @@ -52,7 +52,19 @@ def execute( raise RuntimeError("{} not found".format(cmd)) if wait: - out, _ = proc.communicate() + timeout = 60 + try: + out, _ = proc.communicate(timeout=timeout) + except subprocess.TimeoutExpired as e: + logging.warning( + f""" + Communication with process pid={proc.pid} hangs for more + than {timeout} seconds. + If this is not expected, the process is stale, or + you might have run in stdout / stderr deadlock. + """ + ) + out, _ = proc.communicate() if proc.returncode != 0: err = "{} exited with code {}".format(cmd, proc.returncode) logging.warning(err) From 9170785ed52fe43cd7849f0560934b5e54858778 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Fri, 15 Sep 2023 15:21:38 +0200 Subject: [PATCH 06/10] fix(module-shell): fix syntax error --- bumblebee_status/modules/contrib/shell.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bumblebee_status/modules/contrib/shell.py b/bumblebee_status/modules/contrib/shell.py index 7043778..e8461b9 100644 --- a/bumblebee_status/modules/contrib/shell.py +++ b/bumblebee_status/modules/contrib/shell.py @@ -72,7 +72,6 @@ class Module(core.module.Module): self.__current_thread = threading.Thread( target=lambda obj, cmd: obj.set_output( util.cli.execute(cmd, ignore_errors=True) - core.event.trigger("update", [self.id], redraw_only=True) ), args=(self, self.__command), ) From 9251217bb3aff34a0603b94d2755ef99b87d5d4b Mon Sep 17 00:00:00 2001 From: Elin Angelov Date: Wed, 20 Sep 2023 14:59:24 +0300 Subject: [PATCH 07/10] feat: add `disabled` parameter it pauses dunst notification on startup --- bumblebee_status/modules/contrib/dunstctl.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bumblebee_status/modules/contrib/dunstctl.py b/bumblebee_status/modules/contrib/dunstctl.py index f082f1b..1b5d3fb 100644 --- a/bumblebee_status/modules/contrib/dunstctl.py +++ b/bumblebee_status/modules/contrib/dunstctl.py @@ -28,6 +28,8 @@ class Module(core.module.Module): self.__states = {"unknown": ["unknown", "critical"], "true": ["muted", "warning"], "false": ["unmuted"]} + if util.format.asbool(self.parameter("disabled", False)): + util.cli.execute("dunstctl set-paused true", ignore_errors=True) def toggle_state(self, event): util.cli.execute("dunstctl set-paused toggle", ignore_errors=True) From 0c2123cfe45a2e19c6edd167bfc2c9fd006a7109 Mon Sep 17 00:00:00 2001 From: Elin Angelov Date: Wed, 20 Sep 2023 15:11:24 +0300 Subject: [PATCH 08/10] fix: identation --- bumblebee_status/modules/contrib/dunstctl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bumblebee_status/modules/contrib/dunstctl.py b/bumblebee_status/modules/contrib/dunstctl.py index 1b5d3fb..1ad43df 100644 --- a/bumblebee_status/modules/contrib/dunstctl.py +++ b/bumblebee_status/modules/contrib/dunstctl.py @@ -28,7 +28,7 @@ class Module(core.module.Module): self.__states = {"unknown": ["unknown", "critical"], "true": ["muted", "warning"], "false": ["unmuted"]} - if util.format.asbool(self.parameter("disabled", False)): + if util.format.asbool(self.parameter("disabled", False)): util.cli.execute("dunstctl set-paused true", ignore_errors=True) def toggle_state(self, event): From b217ac9c9ef623e46a2e7d2d76674423d629f350 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Sun, 1 Oct 2023 10:07:03 +0200 Subject: [PATCH 09/10] docs: update module documentation --- docs/modules.rst | 181 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 168 insertions(+), 13 deletions(-) diff --git a/docs/modules.rst b/docs/modules.rst index c7a78de..1fb752c 100644 --- a/docs/modules.rst +++ b/docs/modules.rst @@ -264,6 +264,8 @@ Parameters: * pulsectl.autostart: If set to 'true' (default is 'false'), automatically starts the pulsectl daemon if it is not running * pulsectl.percent_change: How much to change volume by when scrolling on the module (default is 2%) * pulsectl.limit: Upper limit for setting the volume (default is 0%, which means 'no limit') + * pulsectl.popup-filter: Comma-separated list of device strings (if the device name contains it) to exclude + from the default device popup menu (e.g. Monitor for sources) * pulsectl.showbars: 'true' for showing volume bars, requires --markup=pango; 'false' for not showing volume bars (default) * pulsectl.showdevicename: If set to 'true' (default is 'false'), the currently selected default device is shown. @@ -424,6 +426,7 @@ Requires the following executable: * amixer Parameters: + * amixer.card: Sound Card to use (default is 0) * amixer.device: Device to use (default is Master,0) * amixer.percent_change: How much to change volume by when scrolling on the module (default is 4%) @@ -431,6 +434,8 @@ contributed by `zetxx `_ - many thanks! input handling contributed by `ardadem `_ - many thanks! +multiple audio cards contributed by `hugoeustaquio `_ - many thanks! + .. image:: ../screenshots/amixer.png apt @@ -685,6 +690,49 @@ lacking the aforementioned pattern settings or they have wrong values. contributed by `somospocos `_ - many thanks! +cpu3 +~~~~ + +Multiwidget CPU module + +Can display any combination of: + + * max CPU frequency + * total CPU load in percents (integer value) + * per-core CPU load as graph - either mono or colored + * CPU temperature (in Celsius degrees) + * CPU fan speed + +Requirements: + + * the psutil Python module for the first three items from the list above + * sensors executable for the rest + +Parameters: + * cpu3.layout: Space-separated list of widgets to add. + Possible widgets are: + + * cpu3.maxfreq + * cpu3.cpuload + * cpu3.coresload + * cpu3.temp + * cpu3.fanspeed + * cpu3.colored: 1 for colored per core load graph, 0 for mono (default) + * cpu3.temp_json: json path to look for in the output of 'sensors -j'; + required if cpu3.temp widget is used + * cpu3.fan_json: json path to look for in the output of 'sensors -j'; + required if cpu3.fanspeed widget is used + +Note: if you are getting 'n/a' for CPU temperature / fan speed, then you're +lacking the aforementioned json path settings or they have wrong values. + +Example json paths: + * `cpu3.temp_json="coretemp-isa-0000.Package id 0.temp1_input"` + * `cpu3.fan_json="thinkpad-isa-0000.fan1.fan1_input"` + +contributed by `SuperQ ` +based on cpu2 by `` + currency ~~~~~~~~ @@ -866,7 +914,9 @@ 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. +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. @@ -878,7 +928,7 @@ Parameters: Requires these pip packages: * google-api-python-client >= 1.8.0 - * google-auth-httplib2 + * google-auth-httplib2 * google-auth-oauthlib getcrypto @@ -923,6 +973,29 @@ contributed by: .. image:: ../screenshots/github.png +gitlab +~~~~~~ + +Displays the GitLab todo count: + + * https://docs.gitlab.com/ee/user/todos.html + * https://docs.gitlab.com/ee/api/todos.html + +Uses `xdg-open` or `x-www-browser` to open web-pages. + +Requires the following library: + * requests + +Errors: + if the GitLab todo query failed, the shown value is `n/a` + +Parameters: + * gitlab.token: GitLab personal access token, the token needs to have the "read_api" scope. + * gitlab.host: Host of the GitLab instance, default is "gitlab.com". + * gitlab.actions: Comma separated actions to be parsed (e.g.: gitlab.actions=assigned,approval_required) + +.. image:: ../screenshots/gitlab.png + gpmdp ~~~~~ @@ -1107,6 +1180,7 @@ Parameters: if {file} = '/foo/bar.baz', then {file2} = 'bar' * mpd.host: MPD host to connect to. (mpc behaviour by default) + * mpd.port: MPD port to connect to. (mpc behaviour by default) * mpd.layout: Space-separated list of widgets to add. Possible widgets are the buttons/toggles mpd.prev, mpd.next, mpd.shuffle and mpd.repeat, and the main display with play/pause function mpd.main. contributed by `alrayyes `_ - many thanks! @@ -1126,9 +1200,7 @@ network_traffic ~~~~~~~~~~~~~~~ Displays network traffic - -Requires the following library: - * netifaces + * No extra configuration needed contributed by `izn `_ - many thanks! @@ -1155,7 +1227,7 @@ nvidiagpu Displays GPU name, temperature and memory usage. Parameters: - * nvidiagpu.format: Format string (defaults to '{name}: {temp}°C %{mem_used}/{mem_total} MiB') + * 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} {mem_io_pct} Requires nvidia-smi @@ -1239,12 +1311,19 @@ Displays the pi-hole status (up/down) together with the number of ads that were Parameters: * pihole.address : pi-hole address (e.q: http://192.168.1.3) - * pihole.pwhash : pi-hole webinterface password hash (can be obtained from the /etc/pihole/SetupVars.conf file) + + + * pihole.apitoken : pi-hole API token (can be obtained in the pi-hole webinterface (Settings -> API) + + OR (deprecated!) + + * pihole.pwhash : pi-hole webinterface password hash (can be obtained from the /etc/pihole/SetupVars.conf file) + contributed by `bbernhard `_ - many thanks! pipewire -~~~~~~~ +~~~~~~~~ get volume level or control it @@ -1575,7 +1654,9 @@ Display a stock quote from finance.yahoo.com Parameters: * stock.symbols : Comma-separated list of symbols to fetch - * stock.change : Should we fetch change in stock value (defaults to True) + * stock.apikey : API key created on https://alphavantage.co + * stock.url : URL to use, defaults to "https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol={symbol}&apikey={apikey}" + * stock.fields : Fields from the response to show, defaults to "01. symbol,05. price,10. change percent" contributed by `msoulier `_ - many thanks! @@ -1610,11 +1691,11 @@ adds the possibility to * reboot the system. - + Per default a confirmation dialog is shown before the actual action is performed. - + Parameters: - * system.confirm: show confirmation dialog before performing any action (default: true) + * system.confirm: show confirmation dialog before performing any action (default: true) * system.reboot: specify a reboot command (defaults to 'reboot') * system.shutdown: specify a shutdown command (defaults to 'shutdown -h now') * system.logout: specify a logout command (defaults to 'i3exit logout') @@ -1713,6 +1794,27 @@ Parameters: * todo_org.remaining: False by default. When true, will output the number of remaining todos instead of the number completed (i.e. 1/4 means 1 of 4 todos remaining, rather than 1 of 4 todos completed) Based on the todo module by `codingo ` +todoist +~~~~~~~ + +Displays the nº of Todoist tasks that are due: + + * https://developer.todoist.com/rest/v2/#get-active-tasks + +Uses `xdg-open` or `x-www-browser` to open web-pages. + +Requires the following library: + * requests + +Errors: + if the Todoist get active tasks query failed, the shown value is `n/a` + +Parameters: + * todoist.token: Todoist api token, you can get it in https://todoist.com/app/settings/integrations/developer. + * todoist.filter: a filter statement defined by Todoist (https://todoist.com/help/articles/introduction-to-filters), eg: "!assigned to: others & (Overdue | due: today)" + +.. image:: ../screenshots/todoist.png + traffic ~~~~~~~ @@ -1751,6 +1853,27 @@ contributed by `ccoors `_ - many thanks! .. image:: ../screenshots/uptime.png +usage +~~~~~ + +Module for ActivityWatch (https://activitywatch.net/) +Displays the amount of time the system was used actively. + +Requirements: + * sqlite3 module for python + * ActivityWatch + +Errors: + * when you get 'error: unable to open database file', modify the parameter 'database' to your ActivityWatch database file + -> often found by running 'locate aw-server/peewee-sqlite.v2.db' + +Parameters: + * usage.database: path to your database file + * usage.format: Specify what gets printed to the bar + -> use 'HH', 'MM' or 'SS', they will get replaced by the number of hours, minutes and seconds, respectively + +contributed by lasnikr (https://github.com/lasnikr) + vpn ~~~ @@ -1770,6 +1893,34 @@ Displays the VPN profile that is currently in use. contributed by `bbernhard `_ - many thanks! +wakatime +~~~~~~~~ + +Displays the WakaTime daily/weekly/monthly times: + + * https://wakatime.com/developers#stats + +Uses `xdg-open` or `x-www-browser` to open web-pages. + +Requires the following library: + * requests + +Errors: + if the Wakatime status query failed, the shown value is `n/a` + +Parameters: + * wakatime.token: Wakatime secret api key, you can get it in https://wakatime.com/settings/account. + * wakatime.range: Range of the output, default is "Today". Can be one of “Today”, “Yesterday”, “Last 7 Days”, “Last 7 Days from Yesterday”, “Last 14 Days”, “Last 30 Days”, “This Week”, “Last Week”, “This Month”, or “Last Month”. + * wakatime.format: Format of the output, default is "digital" + Valid inputs are: + * "decimal" -> 1.37 + * "digital" -> 1:22 + * "seconds" -> 4931.29 + * "text" -> 1 hr 22 mins + * "%H:%M:%S" -> 01:22:31 (or any other valid format) + +.. image:: ../screenshots/wakatime.png + watson ~~~~~~ @@ -1778,6 +1929,10 @@ Displays the status of watson (time-tracking tool) Requires the following executable: * watson +Parameters: + * watson.format: Output format, defaults to "{project} [{tags}]" + Supported fields are: {project}, {tags}, {relative_start}, {absolute_start} + contributed by `bendardenne `_ - many thanks! weather @@ -1795,7 +1950,7 @@ Parameters: * weather.unit: metric (default), kelvin, imperial * weather.showcity: If set to true, show location information, otherwise hide it (defaults to true) * weather.showminmax: If set to true, show the minimum and maximum temperature, otherwise hide it (defaults to false) - * weather.apikey: API key from http://api.openweathermap.org + * weather.apikey: API key from https://api.openweathermap.org contributed by `TheEdgeOfRage `_ - many thanks! From d30e5c694ea73c3bfcc2f0ce8c0e34226471ac4d Mon Sep 17 00:00:00 2001 From: Elin Angelov Date: Mon, 2 Oct 2023 12:31:15 +0300 Subject: [PATCH 10/10] Update modules.rst --- docs/modules.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/modules.rst b/docs/modules.rst index 1fb752c..0b3ff9d 100644 --- a/docs/modules.rst +++ b/docs/modules.rst @@ -884,6 +884,9 @@ be running. Scripts will be executed when dunst gets unpaused. Requires: * dunst v1.5.0+ +Parameters: + * dunstctl.disabled(Boolean): dunst state on start + contributed by `cristianmiranda `_ - many thanks! contributed by `joachimmathes `_ - many thanks!