diff --git a/.github/workflows/autotest.yml b/.github/workflows/autotest.yml index ec02237..4877a70 100644 --- a/.github/workflows/autotest.yml +++ b/.github/workflows/autotest.yml @@ -29,7 +29,7 @@ jobs: 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 $(cat requirements/modules/*.txt | grep -v power | cut -d ' ' -f 1 | sort -u) + pip install $(cat requirements/modules/*.txt | 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 diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 15f1c60..63fb402 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -6,4 +6,4 @@ python: build: os: ubuntu-22.04 tools: - python: "3" + python: "3.11" diff --git a/bumblebee_status/modules/contrib/battery.py b/bumblebee_status/modules/contrib/battery.py index 21951c1..7c65642 100644 --- a/bumblebee_status/modules/contrib/battery.py +++ b/bumblebee_status/modules/contrib/battery.py @@ -130,14 +130,8 @@ class Module(core.module.Module): log.debug("adding new widget for {}".format(battery)) widget = self.add_widget(full_text=self.capacity, name=battery) - 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(): + for w in self.widgets(): + if util.format.asbool(self.parameter("decorate", True)) == False: widget.set("theme.exclude", "suffix") def hidden(self): @@ -153,16 +147,15 @@ 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) ) - elif capacity < 100: - output = "{}%".format(capacity) else: - output = "" + output = "{}%".format(capacity) if ( util.format.asbool(self.parameter("showremaining", True)) @@ -174,16 +167,6 @@ 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) @@ -193,9 +176,6 @@ 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"] @@ -207,10 +187,16 @@ class Module(core.module.Module): charge = self.__manager.charge_any(self._batteries) else: charge = self.__manager.charge(widget.name) - if charge in ["Discharging", "Unknown"]: + if charge == "Discharging": state.append( "discharging-{}".format( - min([5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100], key=lambda i: abs(i - capacity)) + 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)) ) ) else: diff --git a/bumblebee_status/modules/contrib/bluetooth2.py b/bumblebee_status/modules/contrib/bluetooth2.py index a9742ba..22eae88 100644 --- a/bumblebee_status/modules/contrib/bluetooth2.py +++ b/bumblebee_status/modules/contrib/bluetooth2.py @@ -8,6 +8,7 @@ Parameters: contributed by `martindoublem `_ - many thanks! """ + import os import re import subprocess @@ -21,6 +22,7 @@ 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)) @@ -35,7 +37,7 @@ class Module(core.module.Module): def status(self, widget): """Get status.""" - return self._status if self._status.isdigit() and int(self._status) > 1 else "" + return self._status def update(self): """Update current state.""" @@ -44,7 +46,7 @@ class Module(core.module.Module): ) if state > 0: connected_devices = self.get_connected_devices() - self._status = "{}".format(connected_devices) + self._status = "On - {}".format(connected_devices) else: self._status = "Off" adapters_cmd = "rfkill list | grep Bluetooth" @@ -56,23 +58,31 @@ class Module(core.module.Module): def _toggle(self, widget=None): """Toggle bluetooth state.""" - logging.debug("bt: toggling bluetooth") + if "On" in self._status: + state = "false" + else: + state = "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") + 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) def state(self, widget): """Get current state.""" state = [] - if self._status in [ "No Adapter Found", "Off" ]: + if self._status == "No Adapter Found": state.append("critical") - elif self._status == "0": - state.append("enabled") + elif self._status == "On - 0": + state.append("warning") + elif "On" in self._status and not (self._status == "On - 0"): + state.append("ON") else: - state.append("connected") - state.append("good") - + state.append("critical") return state def get_connected_devices(self): @@ -82,8 +92,12 @@ 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 diff --git a/bumblebee_status/modules/contrib/power-profile.py b/bumblebee_status/modules/contrib/power-profile.py deleted file mode 100644 index 2959391..0000000 --- a/bumblebee_status/modules/contrib/power-profile.py +++ /dev/null @@ -1,99 +0,0 @@ -# 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/bumblebee_status/modules/contrib/shell.py b/bumblebee_status/modules/contrib/shell.py index 4aabb5a..e8461b9 100644 --- a/bumblebee_status/modules/contrib/shell.py +++ b/bumblebee_status/modules/contrib/shell.py @@ -41,7 +41,6 @@ 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: @@ -53,7 +52,6 @@ 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, _): @@ -63,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 diff --git a/bumblebee_status/modules/contrib/vpn.py b/bumblebee_status/modules/contrib/vpn.py index 3381e4f..ae8e748 100644 --- a/bumblebee_status/modules/contrib/vpn.py +++ b/bumblebee_status/modules/contrib/vpn.py @@ -1,5 +1,4 @@ # pylint: disable=C0111,R0903 -# -*- coding: utf-8 -*- """ Displays the VPN profile that is currently in use. @@ -69,7 +68,7 @@ class Module(core.module.Module): def vpn_status(self, widget): if self.__connected_vpn_profile is None: - return "" + return "off" return self.__connected_vpn_profile def __on_vpndisconnect(self): diff --git a/bumblebee_status/modules/contrib/wlrotation.py b/bumblebee_status/modules/contrib/wlrotation.py deleted file mode 100644 index 242c50b..0000000 --- a/bumblebee_status/modules/contrib/wlrotation.py +++ /dev/null @@ -1,126 +0,0 @@ -# pylint: disable=C0111,R0903 -# -*- coding: utf-8 -*- - -"""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 -""" - -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.display = None - display_filter = self.parameter("display", None) - for display in json.loads(util.cli.execute("swaymsg -t get_outputs -r")): - name = display['name'] - 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): - state = [] - state.append("locked" if widget.get("locked", True) else "auto") - return state - -# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/bumblebee_status/modules/core/nic.py b/bumblebee_status/modules/core/nic.py index f153a94..09fe487 100644 --- a/bumblebee_status/modules/core/nic.py +++ b/bumblebee_status/modules/core/nic.py @@ -25,7 +25,6 @@ import subprocess import core.module import core.decorators -import core.input import util.cli import util.format @@ -59,8 +58,6 @@ class Module(core.module.Module): self.iw = shutil.which("iw") self._update_widgets(widgets) - 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): self._update_widgets(self.widgets()) @@ -91,7 +88,9 @@ class Module(core.module.Module): def _iswlan(self, intf): # wifi, wlan, wlp, seems to work for me - return intf.startswith("w") and not intf.startswith("wwan") + if intf.startswith("w"): + return True + return False def _istunnel(self, intf): return intf.startswith("tun") or intf.startswith("wg") diff --git a/bumblebee_status/modules/core/pulsectl.py b/bumblebee_status/modules/core/pulsectl.py index 63f9e36..fd77c26 100644 --- a/bumblebee_status/modules/core/pulsectl.py +++ b/bumblebee_status/modules/core/pulsectl.py @@ -198,10 +198,6 @@ class Module(core.module.Module): def state(self, _): if self.__muted: return ["warning", "muted"] - if self.__volume >= .5: - return ["unmuted", "unmuted-high"] - if self.__volume >= .1: - return ["unmuted", "unmuted-mid"] - return ["unmuted", "unmuted-low"] + return ["unmuted"] # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/docs/modules.rst b/docs/modules.rst index 0b3ff9d..c7a78de 100644 --- a/docs/modules.rst +++ b/docs/modules.rst @@ -264,8 +264,6 @@ 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. @@ -426,7 +424,6 @@ 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%) @@ -434,8 +431,6 @@ 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 @@ -690,49 +685,6 @@ 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 ~~~~~~~~ @@ -884,9 +836,6 @@ 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! @@ -917,9 +866,7 @@ 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. @@ -931,7 +878,7 @@ Parameters: Requires these pip packages: * google-api-python-client >= 1.8.0 - * google-auth-httplib2 + * google-auth-httplib2 * google-auth-oauthlib getcrypto @@ -976,29 +923,6 @@ 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 ~~~~~ @@ -1183,7 +1107,6 @@ 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! @@ -1203,7 +1126,9 @@ network_traffic ~~~~~~~~~~~~~~~ Displays network traffic - * No extra configuration needed + +Requires the following library: + * netifaces contributed by `izn `_ - many thanks! @@ -1230,7 +1155,7 @@ nvidiagpu Displays GPU name, temperature and memory usage. Parameters: - * nvidiagpu.format: Format string (defaults to '{name}: {temp}°C %{usedmem}/{totalmem} MiB') + * nvidiagpu.format: Format string (defaults to '{name}: {temp}°C %{mem_used}/{mem_total} 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 @@ -1314,19 +1239,12 @@ 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.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) - + * 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 @@ -1657,9 +1575,7 @@ Display a stock quote from finance.yahoo.com Parameters: * stock.symbols : Comma-separated list of symbols to fetch - * 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" + * stock.change : Should we fetch change in stock value (defaults to True) contributed by `msoulier `_ - many thanks! @@ -1694,11 +1610,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') @@ -1797,27 +1713,6 @@ 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 ~~~~~~~ @@ -1856,27 +1751,6 @@ 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 ~~~ @@ -1896,34 +1770,6 @@ 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 ~~~~~~ @@ -1932,10 +1778,6 @@ 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 @@ -1953,7 +1795,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 https://api.openweathermap.org + * weather.apikey: API key from http://api.openweathermap.org contributed by `TheEdgeOfRage `_ - many thanks! diff --git a/requirements/modules/power-profile.txt b/requirements/modules/power-profile.txt deleted file mode 100644 index 8f7d255..0000000 --- a/requirements/modules/power-profile.txt +++ /dev/null @@ -1,2 +0,0 @@ -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 deleted file mode 100644 index 43cb14f..0000000 --- a/tests/modules/contrib/test_power-profile.py +++ /dev/null @@ -1,32 +0,0 @@ -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/gruvbox-powerline.json b/themes/gruvbox-powerline.json index e008e3f..d199243 100644 --- a/themes/gruvbox-powerline.json +++ b/themes/gruvbox-powerline.json @@ -9,10 +9,6 @@ "fg": "#fbf1c7", "bg": "#cc241d" }, - "good": { - "fg": "#1d2021", - "bg": "#b8bb26" - }, "default-separators": false, "separator-block-width": 0 }, @@ -38,6 +34,16 @@ "bg": "#859900" } }, + "battery": { + "charged": { + "fg": "#1d2021", + "bg": "#b8bb26" + }, + "AC": { + "fg": "#1d2021", + "bg": "#b8bb26" + } + }, "bluetooth": { "ON": { "fg": "#1d2021", diff --git a/themes/icons/ascii.json b/themes/icons/ascii.json index 4f48e4d..d353386 100644 --- a/themes/icons/ascii.json +++ b/themes/icons/ascii.json @@ -410,8 +410,5 @@ "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 1b6bb2c..60f8f51 100644 --- a/themes/icons/awesome-fonts.json +++ b/themes/icons/awesome-fonts.json @@ -199,14 +199,11 @@ }, "pulseout": { "muted": { - "prefix": "󰝟" + "prefix": "" }, "unmuted": { "prefix": "" - }, - "unmuted-low": { "prefix": "󰕿" }, - "unmuted-mid": { "prefix": "󰖀" }, - "unmuted-high": { "prefix": "󰕾" } + } }, "amixer": { "muted": { @@ -226,7 +223,7 @@ }, "pulsein": { "muted": { - "prefix": "󰍭" + "prefix": "" }, "unmuted": { "prefix": "" @@ -244,22 +241,46 @@ "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": { - "connected": { "prefix": "󰂱" }, - "enabled": { "prefix": "󰂯" }, - "critical": { "prefix": "󰂲" } + "ON": { + "prefix": "" + }, + "warning": { + "prefix": "" + }, + "critical": { + "prefix": "" + } }, "battery-upower": { "charged": { @@ -328,46 +349,136 @@ } }, "battery": { - "charged": { "prefix": "󰂄" }, - "AC": { "suffix": "󱐥" }, - "PEN": { "suffix": "󰏪" }, - "charging": { - "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": "" }, - "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": [ + "", + "", + "", + "", + "" + ], + "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": { @@ -580,7 +691,7 @@ } }, "vpn": { - "prefix": "󰖂" + "prefix": "" }, "system": { "prefix": "  " @@ -628,12 +739,5 @@ }, "thunderbird": { "prefix": "" - }, - "power-profile": { - "prefix": "\uF2C1" - }, - "wlrotation": { - "auto": {"prefix": "󰑵"}, - "locked": {"prefix": "󰑸"} } }