From b217ac9c9ef623e46a2e7d2d76674423d629f350 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Sun, 1 Oct 2023 10:07:03 +0200 Subject: [PATCH 01/29] 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 02/29] 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! From 85760926d7d1f7ddaed9e5096bd0e9ebb70c3267 Mon Sep 17 00:00:00 2001 From: siggi Date: Tue, 3 Oct 2023 22:28:28 +0200 Subject: [PATCH 03/29] Add Power-Profile module --- .../modules/contrib/power-profile.py | 99 +++++++++++++++++++ requirements/modules/power-profile.txt | 2 + tests/modules/contrib/test_power-profile.py | 32 ++++++ themes/icons/ascii.json | 3 + themes/icons/awesome-fonts.json | 3 + 5 files changed, 139 insertions(+) create mode 100644 bumblebee_status/modules/contrib/power-profile.py create mode 100644 requirements/modules/power-profile.txt create mode 100644 tests/modules/contrib/test_power-profile.py diff --git a/bumblebee_status/modules/contrib/power-profile.py b/bumblebee_status/modules/contrib/power-profile.py new file mode 100644 index 0000000..2959391 --- /dev/null +++ b/bumblebee_status/modules/contrib/power-profile.py @@ -0,0 +1,99 @@ +# pylint: disable=C0111,R0903 +""" +Displays the current Power-Profile active + + +Left-Click or Right-Click as well as Scrolling up / down changes the active Power-Profile + +Prerequisites: + * dbus-python + * power-profiles-daemon +""" + +import dbus +import core.module +import core.widget +import core.input + + +class PowerProfileManager: + def __init__(self): + self.POWER_PROFILES_NAME = "net.hadess.PowerProfiles" + self.POWER_PROFILES_PATH = "/net/hadess/PowerProfiles" + self.PP_PROPERTIES_CURRENT_POWER_PROFILE = "ActiveProfile" + self.PP_PROPERTIES_ALL_POWER_PROFILES = "Profiles" + + self.DBUS_PROPERTIES = "org.freedesktop.DBus.Properties" + bus = dbus.SystemBus() + pp_proxy = bus.get_object(self.POWER_PROFILES_NAME, self.POWER_PROFILES_PATH) + self.pp_interface = dbus.Interface(pp_proxy, self.DBUS_PROPERTIES) + + def get_current_power_profile(self): + return self.pp_interface.Get( + self.POWER_PROFILES_NAME, self.PP_PROPERTIES_CURRENT_POWER_PROFILE + ) + + def __get_all_power_profile_names(self): + power_profiles = self.pp_interface.Get( + self.POWER_PROFILES_NAME, self.PP_PROPERTIES_ALL_POWER_PROFILES + ) + power_profiles_names = [] + for pp in power_profiles: + power_profiles_names.append(pp["Profile"]) + + return power_profiles_names + + def next_power_profile(self, event): + all_pp_names = self.__get_all_power_profile_names() + current_pp_index = self.__get_current_pp_index() + next_index = 0 + if current_pp_index != (len(all_pp_names) - 1): + next_index = current_pp_index + 1 + + self.pp_interface.Set( + self.POWER_PROFILES_NAME, + self.PP_PROPERTIES_CURRENT_POWER_PROFILE, + all_pp_names[next_index], + ) + + def prev_power_profile(self, event): + all_pp_names = self.__get_all_power_profile_names() + current_pp_index = self.__get_current_pp_index() + last_index = len(all_pp_names) - 1 + if current_pp_index is not 0: + last_index = current_pp_index - 1 + + self.pp_interface.Set( + self.POWER_PROFILES_NAME, + self.PP_PROPERTIES_CURRENT_POWER_PROFILE, + all_pp_names[last_index], + ) + + def __get_current_pp_index(self): + all_pp_names = self.__get_all_power_profile_names() + current_pp = self.get_current_power_profile() + return all_pp_names.index(current_pp) + + +class Module(core.module.Module): + def __init__(self, config, theme): + super().__init__(config, theme, core.widget.Widget(self.full_text)) + self.pp_manager = PowerProfileManager() + core.input.register( + self, button=core.input.WHEEL_UP, cmd=self.pp_manager.next_power_profile + ) + core.input.register( + self, button=core.input.WHEEL_DOWN, cmd=self.pp_manager.prev_power_profile + ) + core.input.register( + self, button=core.input.LEFT_MOUSE, cmd=self.pp_manager.next_power_profile + ) + core.input.register( + self, button=core.input.RIGHT_MOUSE, cmd=self.pp_manager.prev_power_profile + ) + + def full_text(self, widgets): + return self.pp_manager.get_current_power_profile() + + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/requirements/modules/power-profile.txt b/requirements/modules/power-profile.txt new file mode 100644 index 0000000..8f7d255 --- /dev/null +++ b/requirements/modules/power-profile.txt @@ -0,0 +1,2 @@ +dbus-python +power-profiles-daemon \ No newline at end of file diff --git a/tests/modules/contrib/test_power-profile.py b/tests/modules/contrib/test_power-profile.py new file mode 100644 index 0000000..43cb14f --- /dev/null +++ b/tests/modules/contrib/test_power-profile.py @@ -0,0 +1,32 @@ +from unittest.mock import patch, MagicMock +import unittest +import pytest + +import core.config +import modules.contrib.power_profile + +pytest.importorskip("dbus") + + +def build_powerprofile_module(): + config = core.config.Config([]) + return modules.contrib.power_profile.Module(config=config, theme=None) + + +class TestPowerProfileUnit(unittest.TestCase): + def __get_mock_dbus_get_method(self, mock_system_bus): + return ( + mock_system_bus.return_value.get_object.return_value.get_dbus_method.return_value + ) + + def test_load_module(self): + __import__("modules.contrib.power-profile") + + @patch("dbus.SystemBus") + def test_full_text(self, mock_system_bus): + mock_get = self.__get_mock_dbus_get_method(mock_system_bus) + mock_get.return_value = "balanced" + + module = build_powerprofile_module() + module.update() + assert module.widgets()[0].full_text() == "balanced" diff --git a/themes/icons/ascii.json b/themes/icons/ascii.json index d353386..4f48e4d 100644 --- a/themes/icons/ascii.json +++ b/themes/icons/ascii.json @@ -410,5 +410,8 @@ "speedtest": { "running": { "prefix": [".", "..", "...", ".."] }, "not-running": { "prefix": "[start]" } + }, + "power-profile": { + "prefix": "profile" } } diff --git a/themes/icons/awesome-fonts.json b/themes/icons/awesome-fonts.json index 60f8f51..7b86cb8 100644 --- a/themes/icons/awesome-fonts.json +++ b/themes/icons/awesome-fonts.json @@ -739,5 +739,8 @@ }, "thunderbird": { "prefix": "" + }, + "power-profile": { + "prefix": "\uF2C1" } } From 05622f985ae15d2814e336c75e9a1749a9ee00e3 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Thu, 26 Oct 2023 08:54:13 +0200 Subject: [PATCH 04/29] fix(module/shell): expand user directory in command spec Make sure that ~/ is expanded to the user's home directory in the command specified for the shell module. fixes #1002 --- bumblebee_status/modules/contrib/shell.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bumblebee_status/modules/contrib/shell.py b/bumblebee_status/modules/contrib/shell.py index e8461b9..46352b2 100644 --- a/bumblebee_status/modules/contrib/shell.py +++ b/bumblebee_status/modules/contrib/shell.py @@ -41,6 +41,7 @@ class Module(core.module.Module): super().__init__(config, theme, core.widget.Widget(self.get_output)) self.__command = self.parameter("command", 'echo "no command configured"') + self.__command = os.path.expanduser(self.__command) self.__async = util.format.asbool(self.parameter("async")) if self.__async: From 025d3fcb515be6f1ed3e462a10654aa0ad7ed13f Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Thu, 26 Oct 2023 09:00:09 +0200 Subject: [PATCH 05/29] fix(module/shell): synchonous invocation was broken For some (unknown) reason, redrawing while in the update loop breaks the update (this probably warrants a closer look). As a quickfix to restore functionality, remove the unnecessary redraw call and move it into the async codepath, where it is actually needed. fixes #1001 --- bumblebee_status/modules/contrib/shell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bumblebee_status/modules/contrib/shell.py b/bumblebee_status/modules/contrib/shell.py index 46352b2..4aabb5a 100644 --- a/bumblebee_status/modules/contrib/shell.py +++ b/bumblebee_status/modules/contrib/shell.py @@ -53,6 +53,7 @@ class Module(core.module.Module): def set_output(self, value): self.__output = value + core.event.trigger("update", [self.id], redraw_only=True) @core.decorators.scrollable def get_output(self, _): @@ -62,7 +63,6 @@ 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 From 9f6c9cc7d2481d24b07ea9b84a16057d90fe99e2 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Thu, 26 Oct 2023 09:08:43 +0200 Subject: [PATCH 06/29] doc: update readthedocs.yaml --- .readthedocs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 63fb402..15f1c60 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -6,4 +6,4 @@ python: build: os: ubuntu-22.04 tools: - python: "3.11" + python: "3" From c14ed1166dd2b9a99c896375e989555fcf0c8815 Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Thu, 26 Oct 2023 09:11:39 +0200 Subject: [PATCH 07/29] fix: autotest - try to pin down pip versions --- .github/workflows/autotest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/autotest.yml b/.github/workflows/autotest.yml index 4877a70..ac236f9 100644 --- a/.github/workflows/autotest.yml +++ b/.github/workflows/autotest.yml @@ -28,7 +28,7 @@ jobs: run: | python -m pip install --upgrade pip pip install -U coverage pytest pytest-mock freezegun - pip install 'pygit2<1' 'libvirt-python<6.3' 'feedparser<6' || true + pip install 'pygit2<1' 'libvirt-python<6.3' 'feedparser<6' 'power<1.4' || true pip install $(cat requirements/modules/*.txt | cut -d ' ' -f 1 | sort -u) - name: Install Code Climate dependency run: | From bfafd936433acc9f2dea47065c4e423d7edb033f Mon Sep 17 00:00:00 2001 From: tobi-wan-kenobi Date: Thu, 26 Oct 2023 09:19:46 +0200 Subject: [PATCH 08/29] fix: remove power from tests installation as a quickfix --- .github/workflows/autotest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/autotest.yml b/.github/workflows/autotest.yml index ac236f9..ec02237 100644 --- a/.github/workflows/autotest.yml +++ b/.github/workflows/autotest.yml @@ -28,8 +28,8 @@ jobs: run: | python -m pip install --upgrade pip pip install -U coverage pytest pytest-mock freezegun - pip install 'pygit2<1' 'libvirt-python<6.3' 'feedparser<6' 'power<1.4' || true - pip install $(cat requirements/modules/*.txt | cut -d ' ' -f 1 | sort -u) + pip install 'pygit2<1' 'libvirt-python<6.3' 'feedparser<6' || true + pip install $(cat requirements/modules/*.txt | grep -v power | cut -d ' ' -f 1 | sort -u) - name: Install Code Climate dependency run: | curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter From b45ff330c9d2044dda0a43503fab5d45973bd983 Mon Sep 17 00:00:00 2001 From: Ludwig Behm Date: Wed, 8 Nov 2023 00:28:29 +0100 Subject: [PATCH 09/29] wlrotation: init --- .../modules/contrib/wlrotation.py | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 bumblebee_status/modules/contrib/wlrotation.py diff --git a/bumblebee_status/modules/contrib/wlrotation.py b/bumblebee_status/modules/contrib/wlrotation.py new file mode 100644 index 0000000..54b43a9 --- /dev/null +++ b/bumblebee_status/modules/contrib/wlrotation.py @@ -0,0 +1,69 @@ +# pylint: disable=C0111,R0903 +# -*- coding: utf-8 -*- + +"""Shows a widget for each connected screen and allows the user to loop through different orientations. + +Requires the following executable: + * swaymsg +""" + +import bumblebee.util +import bumblebee.input +import bumblebee.output +import bumblebee.engine +import json +from os import environ, path + +possible_orientations = ["normal", "90", "180", "270"] + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + self.service = "sway-auto-rotate@%s" % path.basename(environ['SWAYSOCK']).replace('-', '\\\\x2d') + widgets = [] + self._engine = engine + super(Module, self).__init__(engine, config, widgets) + self.update_widgets(widgets) + + def update_widgets(self, widgets): + for display in json.loads(bumblebee.util.execute("/usr/bin/swaymsg -t get_outputs -r")): + name = display['name'] + widget = self.widget(name) + if not widget: + widget = bumblebee.output.Widget(name=name, full_text="若 "+name) + widget.set("auto", bumblebee.util.execute("systemctl --user show %s -p ActiveState" % self.service).split("\n")[0].split("=")[1]) + self._engine.input.register_callback(widget, button=bumblebee.input.LEFT_MOUSE, cmd=self._toggle) + self._engine.input.register_callback(widget, button=bumblebee.input.RIGHT_MOUSE, cmd=self._toggle_auto) + widget.set("orientation", display['transform']) + widgets.append(widget) + + def update(self, widgets): + if len(widgets) <= 0: + self.update_widgets(widgets) + + def state(self, widget): + curr = widget.get("auto", "inactive") + if curr == "inactive": + return ["warning"] + return [curr] + + def _toggle_auto(self, event): + widget = self.widget_by_id(event["instance"]) + if widget.get("auto") == "inactive": + bumblebee.util.execute("systemctl --user start %s" % self.service) + else: + bumblebee.util.execute("systemctl --user stop %s" % self.service) + widget.set("auto", bumblebee.util.execute("systemctl --user show %s -p ActiveState" % self.service).split("\n")[0].split("=")[1]) + + def _toggle(self, event): + widget = self.widget_by_id(event["instance"]) + + # compute new orientation based on current orientation + idx = possible_orientations.index(widget.get("orientation")) + idx = (idx + 1) % len(possible_orientations) + new_orientation = possible_orientations[idx] + + widget.set("orientation", new_orientation) + + bumblebee.util.execute("swaymsg 'output {} transform {}'".format(widget.name, new_orientation)) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From 559517f345e95a19e0ff010cae4de3324cd9892d Mon Sep 17 00:00:00 2001 From: Ludwig Behm Date: Wed, 8 Nov 2023 00:31:14 +0100 Subject: [PATCH 10/29] update golor theme and icons --- themes/gruvbox-powerline.json | 14 +-- themes/icons/awesome-fonts.json | 206 +++++++------------------------- 2 files changed, 50 insertions(+), 170 deletions(-) diff --git a/themes/gruvbox-powerline.json b/themes/gruvbox-powerline.json index d199243..e008e3f 100644 --- a/themes/gruvbox-powerline.json +++ b/themes/gruvbox-powerline.json @@ -9,6 +9,10 @@ "fg": "#fbf1c7", "bg": "#cc241d" }, + "good": { + "fg": "#1d2021", + "bg": "#b8bb26" + }, "default-separators": false, "separator-block-width": 0 }, @@ -34,16 +38,6 @@ "bg": "#859900" } }, - "battery": { - "charged": { - "fg": "#1d2021", - "bg": "#b8bb26" - }, - "AC": { - "fg": "#1d2021", - "bg": "#b8bb26" - } - }, "bluetooth": { "ON": { "fg": "#1d2021", diff --git a/themes/icons/awesome-fonts.json b/themes/icons/awesome-fonts.json index 7b86cb8..69cc583 100644 --- a/themes/icons/awesome-fonts.json +++ b/themes/icons/awesome-fonts.json @@ -241,46 +241,22 @@ "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": { @@ -349,136 +325,46 @@ } }, "battery": { - "charged": { - "prefix": "", - "suffix": "" - }, - "AC": { - "suffix": "" - }, + "charged": { "prefix": "" }, + "AC": { "suffix": "ﮣ" }, + "PEN": { "suffix": "" }, "charging": { - "prefix": [ - "", - "", - "", - "", - "" - ], - "suffix": "" + "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": "" - } + "discharging-05": { "prefix": "", "suffix": "" }, + "discharging-10": { "prefix": "", "suffix": "" }, + "discharging-20": { "prefix": "", "suffix": "" }, + "discharging-30": { "prefix": "", "suffix": "" }, + "discharging-40": { "prefix": "", "suffix": "" }, + "discharging-50": { "prefix": "", "suffix": "" }, + "discharging-60": { "prefix": "", "suffix": "" }, + "discharging-70": { "prefix": "", "suffix": "" }, + "discharging-80": { "prefix": "", "suffix": "" }, + "discharging-90": { "prefix": "", "suffix": "" }, + "discharging-100": { "prefix": "" }, + "unlimited": { "prefix": "", "suffix": "" }, + "estimate": { "prefix": "" } }, "battery_all": { - "charged": { - "prefix": "", - "suffix": "" - }, - "AC": { - "suffix": "" - }, + "charged": { "prefix": "", "suffix": "" }, + "AC": { "suffix": "" }, "charging": { - "prefix": [ - "", - "", - "", - "", - "" - ], + "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": "" - } + "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": { @@ -691,7 +577,7 @@ } }, "vpn": { - "prefix": "" + "prefix": "嬨" }, "system": { "prefix": "  " From 61123eb7a06add02d86a437903987799edac4c9e Mon Sep 17 00:00:00 2001 From: Ludwig Behm Date: Wed, 8 Nov 2023 21:52:08 +0100 Subject: [PATCH 11/29] fix wlrotation: migrated to new api --- .../modules/contrib/wlrotation.py | 54 +++++++++---------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/bumblebee_status/modules/contrib/wlrotation.py b/bumblebee_status/modules/contrib/wlrotation.py index 54b43a9..d94c280 100644 --- a/bumblebee_status/modules/contrib/wlrotation.py +++ b/bumblebee_status/modules/contrib/wlrotation.py @@ -7,38 +7,33 @@ Requires the following executable: * swaymsg """ -import bumblebee.util -import bumblebee.input -import bumblebee.output -import bumblebee.engine +import core.module +import core.input +import util.cli + import json from os import environ, path possible_orientations = ["normal", "90", "180", "270"] -class Module(bumblebee.engine.Module): - def __init__(self, engine, config): - self.service = "sway-auto-rotate@%s" % path.basename(environ['SWAYSOCK']).replace('-', '\\\\x2d') - widgets = [] - self._engine = engine - super(Module, self).__init__(engine, config, widgets) - self.update_widgets(widgets) +class Module(core.module.Module): + def __init__(self, config, theme): + super().__init__(config, theme, []) - def update_widgets(self, widgets): - for display in json.loads(bumblebee.util.execute("/usr/bin/swaymsg -t get_outputs -r")): + self.service = "sway-auto-rotate@%s" % path.basename(environ['SWAYSOCK']).replace('-', '\\\\x2d') +# self._widgets = [] + self.update_widgets() + + def update_widgets(self): + for display in json.loads(util.cli.execute("/usr/bin/swaymsg -t get_outputs -r")): name = display['name'] widget = self.widget(name) if not widget: - widget = bumblebee.output.Widget(name=name, full_text="若 "+name) - widget.set("auto", bumblebee.util.execute("systemctl --user show %s -p ActiveState" % self.service).split("\n")[0].split("=")[1]) - self._engine.input.register_callback(widget, button=bumblebee.input.LEFT_MOUSE, cmd=self._toggle) - self._engine.input.register_callback(widget, button=bumblebee.input.RIGHT_MOUSE, cmd=self._toggle_auto) + widget = self.add_widget(name=name, full_text="若 "+name) + widget.set("auto", util.cli.execute("systemctl --user show %s -p ActiveState" % self.service).split("\n")[0].split("=")[1]) + core.input.register(widget, button=core.input.LEFT_MOUSE, cmd=self._toggle) + core.input.register(widget, button=core.input.RIGHT_MOUSE, cmd=self._toggle_auto) widget.set("orientation", display['transform']) - widgets.append(widget) - - def update(self, widgets): - if len(widgets) <= 0: - self.update_widgets(widgets) def state(self, widget): curr = widget.get("auto", "inactive") @@ -47,23 +42,22 @@ class Module(bumblebee.engine.Module): return [curr] def _toggle_auto(self, event): - widget = self.widget_by_id(event["instance"]) + widget = self.widget(widget_id=event["instance"]) if widget.get("auto") == "inactive": - bumblebee.util.execute("systemctl --user start %s" % self.service) + util.cli.execute("systemctl --user start %s" % self.service) else: - bumblebee.util.execute("systemctl --user stop %s" % self.service) - widget.set("auto", bumblebee.util.execute("systemctl --user show %s -p ActiveState" % self.service).split("\n")[0].split("=")[1]) + util.cli.execute("systemctl --user stop %s" % self.service) + widget.set("auto", util.cli.execute("systemctl --user show %s -p ActiveState" % self.service).split("\n")[0].split("=")[1]) def _toggle(self, event): - widget = self.widget_by_id(event["instance"]) + widget = self.widget(widget_id=event["instance"]) # compute new orientation based on current orientation - idx = possible_orientations.index(widget.get("orientation")) - idx = (idx + 1) % len(possible_orientations) + idx = (possible_orientations.index(widget.get("orientation"))+ 1) % len(possible_orientations) new_orientation = possible_orientations[idx] widget.set("orientation", new_orientation) - bumblebee.util.execute("swaymsg 'output {} transform {}'".format(widget.name, new_orientation)) + util.cli.execute("swaymsg 'output {} transform {}'".format(widget.name, new_orientation)) # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From 2e18d712844e60adf690ea575b3a73601cb07537 Mon Sep 17 00:00:00 2001 From: Ludwig Behm Date: Wed, 8 Nov 2023 22:46:58 +0100 Subject: [PATCH 12/29] battery: better support for pen battery and some formatting --- bumblebee_status/modules/contrib/battery.py | 38 ++++++++++++++------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/bumblebee_status/modules/contrib/battery.py b/bumblebee_status/modules/contrib/battery.py index 7c65642..21951c1 100644 --- a/bumblebee_status/modules/contrib/battery.py +++ b/bumblebee_status/modules/contrib/battery.py @@ -130,8 +130,14 @@ class Module(core.module.Module): log.debug("adding new widget for {}".format(battery)) widget = self.add_widget(full_text=self.capacity, name=battery) - for w in self.widgets(): - if util.format.asbool(self.parameter("decorate", True)) == False: + try: + with open("/sys/class/power_supply/{}/model_name".format(battery)) as f: + widget.set("pen", ("Pen" in f.read().strip())) + except Exception: + pass + + if util.format.asbool(self.parameter("decorate", True)) == False: + for widget in self.widgets(): widget.set("theme.exclude", "suffix") def hidden(self): @@ -147,15 +153,16 @@ class Module(core.module.Module): capacity = self.__manager.capacity(widget.name) widget.set("capacity", capacity) widget.set("ac", self.__manager.isac_any(self._batteries)) - widget.set("theme.minwidth", "100%") # Read power conumption if util.format.asbool(self.parameter("showpowerconsumption", False)): output = "{}% ({})".format( capacity, self.__manager.consumption(widget.name) ) - else: + elif capacity < 100: output = "{}%".format(capacity) + else: + output = "" if ( util.format.asbool(self.parameter("showremaining", True)) @@ -167,6 +174,16 @@ class Module(core.module.Module): output, util.format.duration(remaining, compact=True, unit=True) ) +# if bumblebee.util.asbool(self.parameter("rate", True)): +# try: +# with open("{}/power_now".format(widget.name)) as f: +# rate = (float(f.read())/1000000) +# if rate > 0: +# output = "{} {:.2f}w".format(output, rate) +# except Exception: +# pass + + if util.format.asbool(self.parameter("showdevice", False)): output = "{} ({})".format(output, widget.name) @@ -176,6 +193,9 @@ class Module(core.module.Module): state = [] capacity = widget.get("capacity") + if widget.get("pen"): + state.append("PEN") + if capacity < 0: log.debug("battery state: {}".format(state)) return ["critical", "unknown"] @@ -187,16 +207,10 @@ class Module(core.module.Module): charge = self.__manager.charge_any(self._batteries) else: charge = self.__manager.charge(widget.name) - if charge == "Discharging": + if charge in ["Discharging", "Unknown"]: state.append( "discharging-{}".format( - min([10, 25, 50, 80, 100], key=lambda i: abs(i - capacity)) - ) - ) - elif charge == "Unknown": - state.append( - "unknown-{}".format( - min([10, 25, 50, 80, 100], key=lambda i: abs(i - capacity)) + min([5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100], key=lambda i: abs(i - capacity)) ) ) else: From 900a0710c55983173e3a2556c3b86f83ad8e9ee5 Mon Sep 17 00:00:00 2001 From: Ludwig Behm Date: Wed, 8 Nov 2023 22:56:23 +0100 Subject: [PATCH 13/29] nic: add click handler --- bumblebee_status/modules/core/nic.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bumblebee_status/modules/core/nic.py b/bumblebee_status/modules/core/nic.py index 09fe487..64ee226 100644 --- a/bumblebee_status/modules/core/nic.py +++ b/bumblebee_status/modules/core/nic.py @@ -25,6 +25,7 @@ import subprocess import core.module import core.decorators +import core.input import util.cli import util.format @@ -58,6 +59,8 @@ class Module(core.module.Module): self.iw = shutil.which("iw") self._update_widgets(widgets) + core.input.register(self, button=core.input.LEFT_MOUSE, cmd='/usr/local/bin/wifi-menu') + core.input.register(self, button=core.input.RIGHT_MOUSE, cmd='nm-connection-editor') def update(self): self._update_widgets(self.widgets()) @@ -88,7 +91,7 @@ class Module(core.module.Module): def _iswlan(self, intf): # wifi, wlan, wlp, seems to work for me - if intf.startswith("w"): + if intf.startswith("w") and not intf.startswith("wwan"): return True return False From 3ed26c62a5b63ac6ffb951f88af1474665302109 Mon Sep 17 00:00:00 2001 From: Ludwig Behm Date: Wed, 8 Nov 2023 22:59:56 +0100 Subject: [PATCH 14/29] vpn: shorter default label --- bumblebee_status/modules/contrib/vpn.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bumblebee_status/modules/contrib/vpn.py b/bumblebee_status/modules/contrib/vpn.py index ae8e748..3381e4f 100644 --- a/bumblebee_status/modules/contrib/vpn.py +++ b/bumblebee_status/modules/contrib/vpn.py @@ -1,4 +1,5 @@ # pylint: disable=C0111,R0903 +# -*- coding: utf-8 -*- """ Displays the VPN profile that is currently in use. @@ -68,7 +69,7 @@ class Module(core.module.Module): def vpn_status(self, widget): if self.__connected_vpn_profile is None: - return "off" + return "" return self.__connected_vpn_profile def __on_vpndisconnect(self): From 8cc7c9de9b5a185db35bfe645cd1663f4e07be06 Mon Sep 17 00:00:00 2001 From: Ludwig Behm Date: Fri, 10 Nov 2023 16:46:38 +0100 Subject: [PATCH 15/29] icons: fix nerd font battery/ac --- themes/icons/awesome-fonts.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/themes/icons/awesome-fonts.json b/themes/icons/awesome-fonts.json index 69cc583..f5c02c1 100644 --- a/themes/icons/awesome-fonts.json +++ b/themes/icons/awesome-fonts.json @@ -326,7 +326,7 @@ }, "battery": { "charged": { "prefix": "" }, - "AC": { "suffix": "ﮣ" }, + "AC": { "suffix": "󱐥" }, "PEN": { "suffix": "" }, "charging": { "prefix": [ "", "", "", "", "", "", "" ], From c3d4fce74c5f482919e75da955f274390b9437cc Mon Sep 17 00:00:00 2001 From: Ludwig Behm Date: Fri, 10 Nov 2023 16:48:05 +0100 Subject: [PATCH 16/29] nic: fix syntax --- bumblebee_status/modules/core/nic.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bumblebee_status/modules/core/nic.py b/bumblebee_status/modules/core/nic.py index 64ee226..3f77c89 100644 --- a/bumblebee_status/modules/core/nic.py +++ b/bumblebee_status/modules/core/nic.py @@ -91,9 +91,7 @@ class Module(core.module.Module): def _iswlan(self, intf): # wifi, wlan, wlp, seems to work for me - if intf.startswith("w") and not intf.startswith("wwan"): - return True - return False + return intf.startswith("w") and not intf.startswith("wwan") def _istunnel(self, intf): return intf.startswith("tun") or intf.startswith("wg") From 53de1b524a9e1182fd23593cdc44eb337f0ea5b3 Mon Sep 17 00:00:00 2001 From: Ludwig Behm Date: Fri, 10 Nov 2023 16:50:16 +0100 Subject: [PATCH 17/29] bluetooth2: using dbus api, shortening output, some refactoring --- .../modules/contrib/bluetooth2.py | 33 +++++-------------- 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/bumblebee_status/modules/contrib/bluetooth2.py b/bumblebee_status/modules/contrib/bluetooth2.py index 22eae88..76f1da5 100644 --- a/bumblebee_status/modules/contrib/bluetooth2.py +++ b/bumblebee_status/modules/contrib/bluetooth2.py @@ -8,7 +8,6 @@ Parameters: contributed by `martindoublem `_ - many thanks! """ - import os import re import subprocess @@ -22,7 +21,6 @@ import core.input import util.cli - class Module(core.module.Module): def __init__(self, config, theme): super().__init__(config, theme, core.widget.Widget(self.status)) @@ -46,7 +44,7 @@ class Module(core.module.Module): ) if state > 0: connected_devices = self.get_connected_devices() - self._status = "On - {}".format(connected_devices) + self._status = "{}".format(connected_devices) else: self._status = "Off" adapters_cmd = "rfkill list | grep Bluetooth" @@ -58,31 +56,22 @@ class Module(core.module.Module): def _toggle(self, widget=None): """Toggle bluetooth state.""" - if "On" in self._status: - state = "false" - else: - state = "true" - - cmd = ( - "dbus-send --system --print-reply --dest=org.blueman.Mechanism /org/blueman/mechanism org.blueman.Mechanism.SetRfkillState boolean:%s" - % state - ) - logging.debug("bt: toggling bluetooth") - util.cli.execute(cmd, ignore_errors=True) + + SetRfkillState = self._bus.get_object("org.blueman.Mechanism", "/org/blueman/mechanism").get_dbus_method("SetRfkillState", dbus_interface="org.blueman.Mechanism") + SetRfkillState(self._status == "Off") def state(self, widget): """Get current state.""" state = [] - if self._status == "No Adapter Found": + if self._status in [ "No Adapter Found", "Off" ]: state.append("critical") - elif self._status == "On - 0": + elif self._status == "0": state.append("warning") - elif "On" in self._status and not (self._status == "On - 0"): - state.append("ON") else: - state.append("critical") + state.append("ON") + return state def get_connected_devices(self): @@ -92,12 +81,8 @@ class Module(core.module.Module): ).GetManagedObjects() for path, interfaces in objects.items(): if "org.bluez.Device1" in interfaces: - if dbus.Interface( - self._bus.get_object("org.bluez", path), - "org.freedesktop.DBus.Properties", - ).Get("org.bluez.Device1", "Connected"): + if dbus.Interface(self._bus.get_object("org.bluez", path), "org.freedesktop.DBus.Properties", ).Get("org.bluez.Device1", "Connected"): devices += 1 return devices - # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From bbc26c263cb8777a88b8b5e7859c0f5930319cca Mon Sep 17 00:00:00 2001 From: Ludwig Behm Date: Fri, 10 Nov 2023 16:51:07 +0100 Subject: [PATCH 18/29] wlrotation: fix nerd font icon --- bumblebee_status/modules/contrib/wlrotation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bumblebee_status/modules/contrib/wlrotation.py b/bumblebee_status/modules/contrib/wlrotation.py index d94c280..ae9dbe8 100644 --- a/bumblebee_status/modules/contrib/wlrotation.py +++ b/bumblebee_status/modules/contrib/wlrotation.py @@ -29,7 +29,7 @@ class Module(core.module.Module): name = display['name'] widget = self.widget(name) if not widget: - widget = self.add_widget(name=name, full_text="若 "+name) + widget = self.add_widget(name=name, full_text="󰑵 "+name) widget.set("auto", util.cli.execute("systemctl --user show %s -p ActiveState" % self.service).split("\n")[0].split("=")[1]) core.input.register(widget, button=core.input.LEFT_MOUSE, cmd=self._toggle) core.input.register(widget, button=core.input.RIGHT_MOUSE, cmd=self._toggle_auto) From cbd0c58b4a67df4caefd03b93b00fe9416baec18 Mon Sep 17 00:00:00 2001 From: Ludwig Behm Date: Fri, 10 Nov 2023 19:44:33 +0100 Subject: [PATCH 19/29] wlrotation: refactored the entire module and integrated external services --- .../modules/contrib/wlrotation.py | 135 +++++++++++++----- 1 file changed, 99 insertions(+), 36 deletions(-) diff --git a/bumblebee_status/modules/contrib/wlrotation.py b/bumblebee_status/modules/contrib/wlrotation.py index ae9dbe8..242c50b 100644 --- a/bumblebee_status/modules/contrib/wlrotation.py +++ b/bumblebee_status/modules/contrib/wlrotation.py @@ -3,6 +3,10 @@ """Shows a widget for each connected screen and allows the user to loop through different orientations. +Parameters: + * wlrotation.display : Name of the output display that will be rotated + + wlrotation.auto : Boolean value if the display should be rotatet automatic by default + Requires the following executable: * swaymsg """ @@ -11,53 +15,112 @@ import core.module import core.input import util.cli +import iio import json +from math import degrees, atan2, sqrt from os import environ, path possible_orientations = ["normal", "90", "180", "270"] +class iioValue: + def __init__(self, channel): + self.channel = channel + self.scale = self.read('scale') + self.offset = self.read('offset') + + def read(self, attr): + return float(self.channel.attrs[attr].value) + + def value(self): + return (self.read('raw') + self.offset) * self.scale + +class iioAccelDevice: + def __init__(self): + self.ctx = iio.Context() # store ctx pointer + d = self.ctx.find_device('accel_3d') + self.x = iioValue(d.find_channel('accel_x')) + self.y = iioValue(d.find_channel('accel_y')) + self.z = iioValue(d.find_channel('accel_z')) + + def orientation(self): + """ + returns tuple of `[success, value]` where `success` indicates, if an accurate value could be meassured and `value` the sway output api compatible value or `normal` if success is `False` + """ + x_deg, y_deg, z_deg = self._deg() + if abs(z_deg) < 70: # checks if device is angled too shallow + if x_deg >= 70: return True, "270" + if x_deg <= -70: return True, "90" + if abs(x_deg) <= 20: + if y_deg < 0: return True, "normal" + if y_deg > 0: return True, "180" + return False, "normal" + + def _deg(self): + gravity = 9.81 + x, y, z = self.x.value() / gravity, self.y.value() / gravity, self.z.value() / gravity + return degrees(atan2(x, sqrt(pow(y, 2) + pow(z, 2)))), degrees(atan2(y, sqrt(pow(z, 2) + pow(x, 2)))), degrees(atan2(z, sqrt(pow(x, 2) + pow(y, 2)))) + +class Display(): + def __init__(self, name, widget, display_data, auto=False): + self.name = name + self.widget = widget + self.accelDevice = iioAccelDevice() + self._lock_auto_rotation(not auto) + + self.widget.set("orientation", display_data['transform']) + + core.input.register(widget, button=core.input.LEFT_MOUSE, cmd=self.rotate_90deg) + core.input.register(widget, button=core.input.RIGHT_MOUSE, cmd=self.toggle) + + def rotate_90deg(self, event): + # compute new orientation based on current orientation + current = self.widget.get("orientation") + self._set_rotation(possible_orientations[(possible_orientations.index(current) + 1) % len(possible_orientations)]) + # disable auto rotation + self._lock_auto_rotation(True) + + def toggle(self, event): + self._lock_auto_rotation(not self.locked) + + def auto_rotate(self): + # automagically rotate the display based on sensor values + # this is only called if rotation lock is disabled + success, value = self.accelDevice.orientation() + if success: + self._set_rotation(value) + + def _set_rotation(self, new_orientation): + self.widget.set("orientation", new_orientation) + util.cli.execute("swaymsg 'output {} transform {}'".format(self.name, new_orientation)) + + def _lock_auto_rotation(self, locked): + self.locked = locked + self.widget.set("locked", self.locked) + class Module(core.module.Module): + @core.decorators.every(seconds=1) def __init__(self, config, theme): super().__init__(config, theme, []) - self.service = "sway-auto-rotate@%s" % path.basename(environ['SWAYSOCK']).replace('-', '\\\\x2d') -# self._widgets = [] - self.update_widgets() - - def update_widgets(self): - for display in json.loads(util.cli.execute("/usr/bin/swaymsg -t get_outputs -r")): + self.display = None + display_filter = self.parameter("display", None) + for display in json.loads(util.cli.execute("swaymsg -t get_outputs -r")): name = display['name'] - widget = self.widget(name) - if not widget: - widget = self.add_widget(name=name, full_text="󰑵 "+name) - widget.set("auto", util.cli.execute("systemctl --user show %s -p ActiveState" % self.service).split("\n")[0].split("=")[1]) - core.input.register(widget, button=core.input.LEFT_MOUSE, cmd=self._toggle) - core.input.register(widget, button=core.input.RIGHT_MOUSE, cmd=self._toggle_auto) - widget.set("orientation", display['transform']) + if display_filter == None or display_filter == name: + self.display = Display(name, self.add_widget(name=name), display, auto=util.format.asbool(self.parameter("auto", False))) + break # I assume that it makes only sense to rotate a single screen + + def update(self): + if self.display == None: + return + if self.display.locked: + return + + self.display.auto_rotate() def state(self, widget): - curr = widget.get("auto", "inactive") - if curr == "inactive": - return ["warning"] - return [curr] - - def _toggle_auto(self, event): - widget = self.widget(widget_id=event["instance"]) - if widget.get("auto") == "inactive": - util.cli.execute("systemctl --user start %s" % self.service) - else: - util.cli.execute("systemctl --user stop %s" % self.service) - widget.set("auto", util.cli.execute("systemctl --user show %s -p ActiveState" % self.service).split("\n")[0].split("=")[1]) - - def _toggle(self, event): - widget = self.widget(widget_id=event["instance"]) - - # compute new orientation based on current orientation - idx = (possible_orientations.index(widget.get("orientation"))+ 1) % len(possible_orientations) - new_orientation = possible_orientations[idx] - - widget.set("orientation", new_orientation) - - util.cli.execute("swaymsg 'output {} transform {}'".format(widget.name, new_orientation)) + state = [] + state.append("locked" if widget.get("locked", True) else "auto") + return state # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From d7d4603855f004d3ec6b907afd19f97d1300e41f Mon Sep 17 00:00:00 2001 From: Ludwig Behm Date: Fri, 10 Nov 2023 20:03:44 +0100 Subject: [PATCH 20/29] wlrotation: adding awesome-fonts icons --- themes/icons/awesome-fonts.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/themes/icons/awesome-fonts.json b/themes/icons/awesome-fonts.json index f5c02c1..d01f66f 100644 --- a/themes/icons/awesome-fonts.json +++ b/themes/icons/awesome-fonts.json @@ -628,5 +628,9 @@ }, "power-profile": { "prefix": "\uF2C1" + }, + "wlrotation": { + "auto": {"prefix": "󰑵"}, + "locked": {"prefix": "󰑸"} } } From 60bfb19378cf6885f5b16a14a6af3a535404ea40 Mon Sep 17 00:00:00 2001 From: Ludwig Behm Date: Fri, 10 Nov 2023 20:44:16 +0100 Subject: [PATCH 21/29] pulseout: adding nerd font icons for low, mid, heigh, muted --- bumblebee_status/modules/core/pulsectl.py | 6 +++++- themes/icons/awesome-fonts.json | 7 +++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/bumblebee_status/modules/core/pulsectl.py b/bumblebee_status/modules/core/pulsectl.py index fd77c26..63f9e36 100644 --- a/bumblebee_status/modules/core/pulsectl.py +++ b/bumblebee_status/modules/core/pulsectl.py @@ -198,6 +198,10 @@ class Module(core.module.Module): def state(self, _): if self.__muted: return ["warning", "muted"] - return ["unmuted"] + if self.__volume >= .5: + return ["unmuted", "unmuted-high"] + if self.__volume >= .1: + return ["unmuted", "unmuted-mid"] + return ["unmuted", "unmuted-low"] # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/themes/icons/awesome-fonts.json b/themes/icons/awesome-fonts.json index d01f66f..dae3d92 100644 --- a/themes/icons/awesome-fonts.json +++ b/themes/icons/awesome-fonts.json @@ -199,11 +199,14 @@ }, "pulseout": { "muted": { - "prefix": "" + "prefix": "󰝟" }, "unmuted": { "prefix": "" - } + }, + "unmuted-low": { "prefix": "󰕿" }, + "unmuted-mid": { "prefix": "󰖀" }, + "unmuted-high": { "prefix": "󰕾" } }, "amixer": { "muted": { From 2e81eed830fa3e280ca1f7a6c0740e9b44b60062 Mon Sep 17 00:00:00 2001 From: Ludwig Behm Date: Fri, 10 Nov 2023 20:47:35 +0100 Subject: [PATCH 22/29] pulsein: replacing muted nerd font icon with slash in the other direction, just to align it with bluetooth2 --- themes/icons/awesome-fonts.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/themes/icons/awesome-fonts.json b/themes/icons/awesome-fonts.json index dae3d92..77bc4ef 100644 --- a/themes/icons/awesome-fonts.json +++ b/themes/icons/awesome-fonts.json @@ -226,7 +226,7 @@ }, "pulsein": { "muted": { - "prefix": "" + "prefix": "󰍭" }, "unmuted": { "prefix": "" From 255794cd1c258ab63ae0c52a391546fb58445a93 Mon Sep 17 00:00:00 2001 From: Ludwig Behm Date: Fri, 10 Nov 2023 20:51:31 +0100 Subject: [PATCH 23/29] vpn: updating nerd font icon --- themes/icons/awesome-fonts.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/themes/icons/awesome-fonts.json b/themes/icons/awesome-fonts.json index 77bc4ef..ef08f85 100644 --- a/themes/icons/awesome-fonts.json +++ b/themes/icons/awesome-fonts.json @@ -580,7 +580,7 @@ } }, "vpn": { - "prefix": "嬨" + "prefix": "󰖂" }, "system": { "prefix": "  " From 2bbac991dbff1b5ae74a59c483de1fd2f4fa9e27 Mon Sep 17 00:00:00 2001 From: Ludwig Behm Date: Fri, 10 Nov 2023 21:03:43 +0100 Subject: [PATCH 24/29] battery: updating nerd font icons --- themes/icons/awesome-fonts.json | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/themes/icons/awesome-fonts.json b/themes/icons/awesome-fonts.json index ef08f85..8d77244 100644 --- a/themes/icons/awesome-fonts.json +++ b/themes/icons/awesome-fonts.json @@ -330,22 +330,22 @@ "battery": { "charged": { "prefix": "" }, "AC": { "suffix": "󱐥" }, - "PEN": { "suffix": "" }, + "PEN": { "suffix": "󰏪" }, "charging": { - "prefix": [ "", "", "", "", "", "", "" ], + "prefix": [ "󰢜", "󰂆", "󰂇", "󰂈", "󰢝", "󰂉", "󰢞", "󰂊", "󰂋", "󰂅" ], "suffix": "" }, - "discharging-05": { "prefix": "", "suffix": "" }, - "discharging-10": { "prefix": "", "suffix": "" }, - "discharging-20": { "prefix": "", "suffix": "" }, - "discharging-30": { "prefix": "", "suffix": "" }, - "discharging-40": { "prefix": "", "suffix": "" }, - "discharging-50": { "prefix": "", "suffix": "" }, - "discharging-60": { "prefix": "", "suffix": "" }, - "discharging-70": { "prefix": "", "suffix": "" }, - "discharging-80": { "prefix": "", "suffix": "" }, - "discharging-90": { "prefix": "", "suffix": "" }, - "discharging-100": { "prefix": "" }, + "discharging-05": { "prefix": "󰂎", "suffix": "" }, + "discharging-10": { "prefix": "󰁺", "suffix": "" }, + "discharging-20": { "prefix": "󰁻", "suffix": "" }, + "discharging-30": { "prefix": "󰁼", "suffix": "" }, + "discharging-40": { "prefix": "󰁽", "suffix": "" }, + "discharging-50": { "prefix": "󰁾", "suffix": "" }, + "discharging-60": { "prefix": "󰁿", "suffix": "" }, + "discharging-70": { "prefix": "󰂀", "suffix": "" }, + "discharging-80": { "prefix": "󰂁", "suffix": "" }, + "discharging-90": { "prefix": "󰂂", "suffix": "" }, + "discharging-100": { "prefix": "󰁹" }, "unlimited": { "prefix": "", "suffix": "" }, "estimate": { "prefix": "" } }, From fd4b940d5895298d0f0ecd664cfd47d88940e98a Mon Sep 17 00:00:00 2001 From: Ludwig Behm Date: Wed, 6 Dec 2023 22:01:20 +0100 Subject: [PATCH 25/29] nic.py: adapting wifi-menu path --- 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 3f77c89..f153a94 100644 --- a/bumblebee_status/modules/core/nic.py +++ b/bumblebee_status/modules/core/nic.py @@ -59,7 +59,7 @@ class Module(core.module.Module): self.iw = shutil.which("iw") self._update_widgets(widgets) - core.input.register(self, button=core.input.LEFT_MOUSE, cmd='/usr/local/bin/wifi-menu') + core.input.register(self, button=core.input.LEFT_MOUSE, cmd='wifi-menu') core.input.register(self, button=core.input.RIGHT_MOUSE, cmd='nm-connection-editor') def update(self): From a56b86100a6340bc1dfe7b57667c74ac61f6773f Mon Sep 17 00:00:00 2001 From: Ludwig Behm Date: Wed, 6 Dec 2023 22:17:49 +0100 Subject: [PATCH 26/29] fix updated nerdfonts --- themes/icons/awesome-fonts.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/themes/icons/awesome-fonts.json b/themes/icons/awesome-fonts.json index 8d77244..1e8d9df 100644 --- a/themes/icons/awesome-fonts.json +++ b/themes/icons/awesome-fonts.json @@ -252,13 +252,13 @@ "tunnel-down": { "prefix": "嬨" } }, "bluetooth": { - "ON": { "prefix": "" }, - "OFF": { "prefix": "" }, + "ON": { "prefix": "󰂯" }, + "OFF": { "prefix": "󰂲" }, "?": { "prefix": "" } }, "bluetooth2": { - "ON": { "prefix": "" }, - "warning": { "prefix": "" }, + "ON": { "prefix": "󰂯" }, + "warning": { "prefix": "󰂲" }, "critical": { "prefix": "" } }, "battery-upower": { @@ -328,7 +328,7 @@ } }, "battery": { - "charged": { "prefix": "" }, + "charged": { "prefix": "󰂄" }, "AC": { "suffix": "󱐥" }, "PEN": { "suffix": "󰏪" }, "charging": { From 21060a10a01cd6f1d0721090f8b0a53c60265bbc Mon Sep 17 00:00:00 2001 From: Ludwig Behm Date: Wed, 17 Apr 2024 22:46:18 +0200 Subject: [PATCH 27/29] bluetooth2: only show connection count in cases of excessive numbers of connections --- bumblebee_status/modules/contrib/bluetooth2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bumblebee_status/modules/contrib/bluetooth2.py b/bumblebee_status/modules/contrib/bluetooth2.py index 76f1da5..deb2e0c 100644 --- a/bumblebee_status/modules/contrib/bluetooth2.py +++ b/bumblebee_status/modules/contrib/bluetooth2.py @@ -35,7 +35,7 @@ class Module(core.module.Module): def status(self, widget): """Get status.""" - return self._status + return self._status if self._status.isdigit() and int(self._status) > 1 else "" def update(self): """Update current state.""" From 5053bb0f1b300e96ade9bbbc47fb5f662490dcd9 Mon Sep 17 00:00:00 2001 From: Ludwig Behm Date: Wed, 17 Apr 2024 22:47:45 +0200 Subject: [PATCH 28/29] themes/awesome-fonts: fix bluetooth icons --- themes/icons/awesome-fonts.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/themes/icons/awesome-fonts.json b/themes/icons/awesome-fonts.json index 8d77244..d81b8c8 100644 --- a/themes/icons/awesome-fonts.json +++ b/themes/icons/awesome-fonts.json @@ -252,14 +252,14 @@ "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": { From 676bbebf4ca826c0adab0cc2d2f9ab9a42a572a2 Mon Sep 17 00:00:00 2001 From: Ludwig Behm Date: Wed, 17 Apr 2024 23:05:16 +0200 Subject: [PATCH 29/29] bluetooth2: states and styling --- bumblebee_status/modules/contrib/bluetooth2.py | 5 +++-- themes/icons/awesome-fonts.json | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/bumblebee_status/modules/contrib/bluetooth2.py b/bumblebee_status/modules/contrib/bluetooth2.py index deb2e0c..a9742ba 100644 --- a/bumblebee_status/modules/contrib/bluetooth2.py +++ b/bumblebee_status/modules/contrib/bluetooth2.py @@ -68,9 +68,10 @@ class Module(core.module.Module): if self._status in [ "No Adapter Found", "Off" ]: state.append("critical") elif self._status == "0": - state.append("warning") + state.append("enabled") else: - state.append("ON") + state.append("connected") + state.append("good") return state diff --git a/themes/icons/awesome-fonts.json b/themes/icons/awesome-fonts.json index 9992cee..1b6bb2c 100644 --- a/themes/icons/awesome-fonts.json +++ b/themes/icons/awesome-fonts.json @@ -257,8 +257,8 @@ "?": { "prefix": "󰂱" } }, "bluetooth2": { - "ON": { "prefix": "󰂱" }, - "warning": { "prefix": "󰂯" }, + "connected": { "prefix": "󰂱" }, + "enabled": { "prefix": "󰂯" }, "critical": { "prefix": "󰂲" } }, "battery-upower": {