Compare commits
34 commits
Author | SHA1 | Date | |
---|---|---|---|
676bbebf4c | |||
617a12f96d | |||
5053bb0f1b | |||
21060a10a0 | |||
a56b86100a | |||
fd4b940d58 | |||
2bbac991db | |||
255794cd1c | |||
2e81eed830 | |||
60bfb19378 | |||
d7d4603855 | |||
cbd0c58b4a | |||
bbc26c263c | |||
53de1b524a | |||
c3d4fce74c | |||
8cc7c9de9b | |||
3ed26c62a5 | |||
900a0710c5 | |||
2e18d71284 | |||
61123eb7a0 | |||
27be30263f | |||
559517f345 | |||
b45ff330c9 | |||
|
bfafd93643 | ||
|
c14ed1166d | ||
|
9f6c9cc7d2 | ||
|
025d3fcb51 | ||
|
05622f985a | ||
|
c4a3f488aa | ||
|
68de299763 | ||
|
85760926d7 | ||
|
3bc3c75ff4 | ||
|
d30e5c694e | ||
|
b217ac9c9e |
16 changed files with 549 additions and 232 deletions
2
.github/workflows/autotest.yml
vendored
2
.github/workflows/autotest.yml
vendored
|
@ -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 | cut -d ' ' -f 1 | sort -u)
|
||||
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
|
||||
|
|
|
@ -6,4 +6,4 @@ python:
|
|||
build:
|
||||
os: ubuntu-22.04
|
||||
tools:
|
||||
python: "3.11"
|
||||
python: "3"
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -8,7 +8,6 @@ Parameters:
|
|||
contributed by `martindoublem <https://github.com/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))
|
||||
|
@ -37,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."""
|
||||
|
@ -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,23 @@ 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":
|
||||
state.append("warning")
|
||||
elif "On" in self._status and not (self._status == "On - 0"):
|
||||
state.append("ON")
|
||||
elif self._status == "0":
|
||||
state.append("enabled")
|
||||
else:
|
||||
state.append("critical")
|
||||
state.append("connected")
|
||||
state.append("good")
|
||||
|
||||
return state
|
||||
|
||||
def get_connected_devices(self):
|
||||
|
@ -92,12 +82,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
|
||||
|
|
99
bumblebee_status/modules/contrib/power-profile.py
Normal file
99
bumblebee_status/modules/contrib/power-profile.py
Normal file
|
@ -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
|
|
@ -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:
|
||||
|
@ -52,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, _):
|
||||
|
@ -61,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
|
||||
|
|
|
@ -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):
|
||||
|
|
126
bumblebee_status/modules/contrib/wlrotation.py
Normal file
126
bumblebee_status/modules/contrib/wlrotation.py
Normal file
|
@ -0,0 +1,126 @@
|
|||
# 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
|
|
@ -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='wifi-menu')
|
||||
core.input.register(self, button=core.input.RIGHT_MOUSE, cmd='nm-connection-editor')
|
||||
|
||||
def update(self):
|
||||
self._update_widgets(self.widgets())
|
||||
|
@ -88,9 +91,7 @@ class Module(core.module.Module):
|
|||
|
||||
def _iswlan(self, intf):
|
||||
# wifi, wlan, wlp, seems to work for me
|
||||
if intf.startswith("w"):
|
||||
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")
|
||||
|
|
|
@ -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
|
||||
|
|
184
docs/modules.rst
184
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 <https://github.com/zetxx>`_ - many thanks!
|
|||
|
||||
input handling contributed by `ardadem <https://github.com/ardadem>`_ - many thanks!
|
||||
|
||||
multiple audio cards contributed by `hugoeustaquio <https://github.com/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 <https://github.com/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 <https://github.com/SuperQ>`
|
||||
based on cpu2 by `<somospocos <https://github.com/somospocos>`
|
||||
|
||||
currency
|
||||
~~~~~~~~
|
||||
|
||||
|
@ -836,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 <https://github.com/cristianmiranda>`_ - many thanks!
|
||||
contributed by `joachimmathes <https://github.com/joachimmathes>`_ - many thanks!
|
||||
|
||||
|
@ -866,7 +917,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 +931,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 +976,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 +1183,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 <https://github.com/alrayyes>`_ - many thanks!
|
||||
|
@ -1126,9 +1203,7 @@ network_traffic
|
|||
~~~~~~~~~~~~~~~
|
||||
|
||||
Displays network traffic
|
||||
|
||||
Requires the following library:
|
||||
* netifaces
|
||||
* No extra configuration needed
|
||||
|
||||
contributed by `izn <https://github.com/izn>`_ - many thanks!
|
||||
|
||||
|
@ -1155,7 +1230,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 +1314,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 <https://github.com/bbernhard>`_ - many thanks!
|
||||
|
||||
pipewire
|
||||
~~~~~~~
|
||||
~~~~~~~~
|
||||
|
||||
get volume level or control it
|
||||
|
||||
|
@ -1575,7 +1657,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 <https://github.com/msoulier>`_ - many thanks!
|
||||
|
@ -1610,11 +1694,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 +1797,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 <https://github.com/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 +1856,27 @@ contributed by `ccoors <https://github.com/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 +1896,34 @@ Displays the VPN profile that is currently in use.
|
|||
|
||||
contributed by `bbernhard <https://github.com/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 +1932,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 <https://github.com/bendardenne>`_ - many thanks!
|
||||
|
||||
weather
|
||||
|
@ -1795,7 +1953,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 <https://github.com/TheEdgeOfRage>`_ - many thanks!
|
||||
|
|
2
requirements/modules/power-profile.txt
Normal file
2
requirements/modules/power-profile.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
dbus-python
|
||||
power-profiles-daemon
|
32
tests/modules/contrib/test_power-profile.py
Normal file
32
tests/modules/contrib/test_power-profile.py
Normal file
|
@ -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"
|
|
@ -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",
|
||||
|
|
|
@ -410,5 +410,8 @@
|
|||
"speedtest": {
|
||||
"running": { "prefix": [".", "..", "...", ".."] },
|
||||
"not-running": { "prefix": "[start]" }
|
||||
},
|
||||
"power-profile": {
|
||||
"prefix": "profile"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -199,11 +199,14 @@
|
|||
},
|
||||
"pulseout": {
|
||||
"muted": {
|
||||
"prefix": ""
|
||||
"prefix": ""
|
||||
},
|
||||
"unmuted": {
|
||||
"prefix": ""
|
||||
}
|
||||
},
|
||||
"unmuted-low": { "prefix": "" },
|
||||
"unmuted-mid": { "prefix": "" },
|
||||
"unmuted-high": { "prefix": "" }
|
||||
},
|
||||
"amixer": {
|
||||
"muted": {
|
||||
|
@ -223,7 +226,7 @@
|
|||
},
|
||||
"pulsein": {
|
||||
"muted": {
|
||||
"prefix": ""
|
||||
"prefix": ""
|
||||
},
|
||||
"unmuted": {
|
||||
"prefix": ""
|
||||
|
@ -241,46 +244,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": ""
|
||||
}
|
||||
"connected": { "prefix": "" },
|
||||
"enabled": { "prefix": "" },
|
||||
"critical": { "prefix": "" }
|
||||
},
|
||||
"battery-upower": {
|
||||
"charged": {
|
||||
|
@ -349,136 +328,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 +580,7 @@
|
|||
}
|
||||
},
|
||||
"vpn": {
|
||||
"prefix": ""
|
||||
"prefix": ""
|
||||
},
|
||||
"system": {
|
||||
"prefix": " "
|
||||
|
@ -739,5 +628,12 @@
|
|||
},
|
||||
"thunderbird": {
|
||||
"prefix": ""
|
||||
},
|
||||
"power-profile": {
|
||||
"prefix": "\uF2C1"
|
||||
},
|
||||
"wlrotation": {
|
||||
"auto": {"prefix": ""},
|
||||
"locked": {"prefix": ""}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue