Merge branch 'tobi-wan-kenobi:main' into main
This commit is contained in:
commit
3de6f9f4b9
16 changed files with 468 additions and 13 deletions
70
.github/workflows/codeql-analysis.yml
vendored
Normal file
70
.github/workflows/codeql-analysis.yml
vendored
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
# For most projects, this workflow file will not need changing; you simply need
|
||||||
|
# to commit it to your repository.
|
||||||
|
#
|
||||||
|
# You may wish to alter this file to override the set of languages analyzed,
|
||||||
|
# or to provide custom queries or build logic.
|
||||||
|
#
|
||||||
|
# ******** NOTE ********
|
||||||
|
# We have attempted to detect the languages in your repository. Please check
|
||||||
|
# the `language` matrix defined below to confirm you have the correct set of
|
||||||
|
# supported CodeQL languages.
|
||||||
|
#
|
||||||
|
name: "CodeQL"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
pull_request:
|
||||||
|
# The branches below must be a subset of the branches above
|
||||||
|
branches: [ main ]
|
||||||
|
schedule:
|
||||||
|
- cron: '31 0 * * 4'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analyze:
|
||||||
|
name: Analyze
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
actions: read
|
||||||
|
contents: read
|
||||||
|
security-events: write
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
language: [ 'python' ]
|
||||||
|
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||||
|
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
# Initializes the CodeQL tools for scanning.
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@v1
|
||||||
|
with:
|
||||||
|
languages: ${{ matrix.language }}
|
||||||
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
|
# By default, queries listed here will override any specified in a config file.
|
||||||
|
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||||
|
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||||
|
|
||||||
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
|
- name: Autobuild
|
||||||
|
uses: github/codeql-action/autobuild@v1
|
||||||
|
|
||||||
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
|
# 📚 https://git.io/JvXDl
|
||||||
|
|
||||||
|
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||||
|
# and modify them (or add more) to build your code if your project
|
||||||
|
# uses a compiled language
|
||||||
|
|
||||||
|
#- run: |
|
||||||
|
# make bootstrap
|
||||||
|
# make release
|
||||||
|
|
||||||
|
- name: Perform CodeQL Analysis
|
||||||
|
uses: github/codeql-action/analyze@v1
|
|
@ -8,6 +8,7 @@
|
||||||
[![Code Climate](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status/badges/gpa.svg)](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status)
|
[![Code Climate](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status/badges/gpa.svg)](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status)
|
||||||
[![Test Coverage](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status/badges/coverage.svg)](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status/coverage)
|
[![Test Coverage](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status/badges/coverage.svg)](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status/coverage)
|
||||||
[![Issue Count](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status/badges/issue_count.svg)](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status)
|
[![Issue Count](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status/badges/issue_count.svg)](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status)
|
||||||
|
[![CodeQL](https://github.com/tobi-wan-kenobi/bumblebee-status/actions/workflows/codeql-analysis.yml/badge.svg?branch=main)](https://github.com/tobi-wan-kenobi/bumblebee-status/actions/workflows/codeql-analysis.yml)
|
||||||
![License](https://img.shields.io/github/license/tobi-wan-kenobi/bumblebee-status)
|
![License](https://img.shields.io/github/license/tobi-wan-kenobi/bumblebee-status)
|
||||||
|
|
||||||
**Many, many thanks to all contributors! I am still amazed by and deeply grateful for how many PRs this project gets.**
|
**Many, many thanks to all contributors! I am still amazed by and deeply grateful for how many PRs this project gets.**
|
||||||
|
|
|
@ -54,8 +54,11 @@ def register(obj, button=None, cmd=None, wait=False):
|
||||||
event_id = __event_id(obj.id if obj is not None else "", button)
|
event_id = __event_id(obj.id if obj is not None else "", button)
|
||||||
logging.debug("registering callback {}".format(event_id))
|
logging.debug("registering callback {}".format(event_id))
|
||||||
core.event.unregister(event_id) # make sure there's always only one input event
|
core.event.unregister(event_id) # make sure there's always only one input event
|
||||||
|
|
||||||
if callable(cmd):
|
if callable(cmd):
|
||||||
core.event.register_exclusive(event_id, cmd)
|
core.event.register_exclusive(event_id, cmd)
|
||||||
|
elif obj and hasattr(obj, cmd) and callable(getattr(obj, cmd)):
|
||||||
|
core.event.register_exclusive(event_id, lambda event: getattr(obj, cmd)(event))
|
||||||
else:
|
else:
|
||||||
core.event.register_exclusive(event_id, lambda event: __execute(event, cmd, wait))
|
core.event.register_exclusive(event_id, lambda event: __execute(event, cmd, wait))
|
||||||
|
|
||||||
|
|
141
bumblebee_status/modules/contrib/pactl.py
Normal file
141
bumblebee_status/modules/contrib/pactl.py
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
# pylint: disable=C0111,R0903
|
||||||
|
|
||||||
|
""" Displays the current default sink.
|
||||||
|
|
||||||
|
Left click opens a popup menu that lists all available sinks and allows to change the default sink.
|
||||||
|
|
||||||
|
Per default, this module uses the sink names returned by "pactl list sinks short"
|
||||||
|
|
||||||
|
sample output of "pactl list sinks short":
|
||||||
|
|
||||||
|
2 alsa_output.pci-0000_00_1f.3.analog-stereo module-alsa-card.c s16le 2ch 44100Hz SUSPENDED
|
||||||
|
3 alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo module-alsa-card.c s16le 2ch 44100Hz SUSPENDE
|
||||||
|
|
||||||
|
As "alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo" is not a particularly nice name, its possible to map the name to more a
|
||||||
|
user friendly name. e.g to map "alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo" to the name "Headset", add the following
|
||||||
|
bumblebee-status config entry: pactl.alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo=Headset
|
||||||
|
|
||||||
|
The module also allows to specify individual (unicode) icons for all the sinks. e.g in order to use the icon 🎧 for the
|
||||||
|
"alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo" sink, add the following bumblebee-status config entry:
|
||||||
|
pactl.icon.alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo=🎧
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
* pulseaudio
|
||||||
|
* pactl
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import functools
|
||||||
|
|
||||||
|
import core.module
|
||||||
|
import core.widget
|
||||||
|
import core.input
|
||||||
|
|
||||||
|
import util.cli
|
||||||
|
import util.popup
|
||||||
|
|
||||||
|
|
||||||
|
class Sink(object):
|
||||||
|
def __init__(self, id, internal_name, friendly_name, icon):
|
||||||
|
super().__init__()
|
||||||
|
self.__id = id
|
||||||
|
self.__internal_name = internal_name
|
||||||
|
self.__friendly_name = friendly_name
|
||||||
|
self.__icon = icon
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
return self.__id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def internal_name(self):
|
||||||
|
return self.__internal_name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def friendly_name(self):
|
||||||
|
return self.__friendly_name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def icon(self):
|
||||||
|
return self.__icon
|
||||||
|
|
||||||
|
@property
|
||||||
|
def display_name(self):
|
||||||
|
display_name = (
|
||||||
|
self.__icon + " " + self.__friendly_name
|
||||||
|
if self.__icon != ""
|
||||||
|
else self.__friendly_name
|
||||||
|
)
|
||||||
|
return display_name
|
||||||
|
|
||||||
|
|
||||||
|
class Module(core.module.Module):
|
||||||
|
def __init__(self, config, theme):
|
||||||
|
super().__init__(config, theme, core.widget.Widget(self.default_sink))
|
||||||
|
|
||||||
|
self.__default_sink = None
|
||||||
|
|
||||||
|
res = util.cli.execute("pactl list sinks short")
|
||||||
|
lines = res.splitlines()
|
||||||
|
|
||||||
|
self.__sinks = []
|
||||||
|
for line in lines:
|
||||||
|
info = line.split("\t")
|
||||||
|
try:
|
||||||
|
friendly_name = self.parameter(info[1], info[1])
|
||||||
|
icon = self.parameter("icon." + info[1], "")
|
||||||
|
self.__sinks.append(Sink(info[0], info[1], friendly_name, icon))
|
||||||
|
except:
|
||||||
|
logging.exception("Couldn't parse sink")
|
||||||
|
pass
|
||||||
|
|
||||||
|
core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.popup)
|
||||||
|
|
||||||
|
def __sink(self, internal_sink_name):
|
||||||
|
for sink in self.__sinks:
|
||||||
|
if internal_sink_name == sink.internal_name:
|
||||||
|
return sink
|
||||||
|
return None
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
try:
|
||||||
|
res = util.cli.execute("pactl info")
|
||||||
|
lines = res.splitlines()
|
||||||
|
self.__default_sink = None
|
||||||
|
for line in lines:
|
||||||
|
if not line.startswith("Default Sink:"):
|
||||||
|
continue
|
||||||
|
internal_sink_name = line.replace("Default Sink: ", "")
|
||||||
|
self.__default_sink = self.__sink(internal_sink_name)
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception("Could not get pactl info")
|
||||||
|
self.__default_sink = None
|
||||||
|
|
||||||
|
def default_sink(self, widget):
|
||||||
|
if self.__default_sink is None:
|
||||||
|
return "unknown"
|
||||||
|
return self.__default_sink.display_name
|
||||||
|
|
||||||
|
def __on_sink_selected(self, sink):
|
||||||
|
try:
|
||||||
|
util.cli.execute("pactl set-default-sink {}".format(sink.id))
|
||||||
|
self.__default_sink = sink
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception("Couldn't set default sink")
|
||||||
|
|
||||||
|
def popup(self, widget):
|
||||||
|
menu = util.popup.menu()
|
||||||
|
|
||||||
|
for sink in self.__sinks:
|
||||||
|
menu.add_menuitem(
|
||||||
|
sink.friendly_name,
|
||||||
|
callback=functools.partial(self.__on_sink_selected, sink),
|
||||||
|
)
|
||||||
|
menu.show(widget)
|
||||||
|
|
||||||
|
def state(self, widget):
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|
45
bumblebee_status/modules/contrib/persian_date.py
Normal file
45
bumblebee_status/modules/contrib/persian_date.py
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
# pylint: disable=C0111,R0903
|
||||||
|
|
||||||
|
"""Displays the current date and time in Persian(Jalali) Calendar.
|
||||||
|
|
||||||
|
Requires the following python packages:
|
||||||
|
* jdatetime
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
* datetime.format: strftime()-compatible formatting string. default: "%A %d %B" e.g., "جمعه ۱۳ اسفند"
|
||||||
|
* datetime.locale: locale to use. default: "fa_IR"
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import absolute_import
|
||||||
|
import jdatetime
|
||||||
|
import locale
|
||||||
|
|
||||||
|
import core.module
|
||||||
|
import core.widget
|
||||||
|
import core.input
|
||||||
|
|
||||||
|
|
||||||
|
class Module(core.module.Module):
|
||||||
|
def __init__(self, config, theme):
|
||||||
|
super().__init__(config, theme, core.widget.Widget(self.full_text))
|
||||||
|
|
||||||
|
l = ("fa_IR", "UTF-8")
|
||||||
|
lcl = self.parameter("locale", ".".join(l))
|
||||||
|
try:
|
||||||
|
locale.setlocale(locale.LC_ALL, lcl.split("."))
|
||||||
|
except Exception as e:
|
||||||
|
locale.setlocale(locale.LC_ALL, ("fa_IR", "UTF-8"))
|
||||||
|
|
||||||
|
def default_format(self):
|
||||||
|
return "%A %d %B"
|
||||||
|
|
||||||
|
def full_text(self, widget):
|
||||||
|
enc = locale.getpreferredencoding()
|
||||||
|
fmt = self.parameter("format", self.default_format())
|
||||||
|
retval = jdatetime.datetime.now().strftime(fmt)
|
||||||
|
if hasattr(retval, "decode"):
|
||||||
|
return retval.decode(enc)
|
||||||
|
return retval
|
||||||
|
|
||||||
|
|
||||||
|
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|
|
@ -16,13 +16,13 @@ class Module(core.module.Module):
|
||||||
self.__ip = ""
|
self.__ip = ""
|
||||||
|
|
||||||
def public_ip(self, widget):
|
def public_ip(self, widget):
|
||||||
return self.__ip
|
return self.__ip or "n/a"
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
try:
|
try:
|
||||||
self.__ip = util.location.public_ip()
|
self.__ip = util.location.public_ip()
|
||||||
except Exception:
|
except Exception:
|
||||||
self.__ip = "n/a"
|
self.__ip = None
|
||||||
|
|
||||||
|
|
||||||
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|
||||||
|
|
|
@ -55,7 +55,7 @@ class Module(core.module.Module):
|
||||||
|
|
||||||
self._state = []
|
self._state = []
|
||||||
|
|
||||||
self._newspaper_filename = tempfile.mktemp(".html")
|
self._newspaper_file = tempfile.NamedTemporaryFile(mode="w", suffix=".html")
|
||||||
|
|
||||||
self._last_refresh = 0
|
self._last_refresh = 0
|
||||||
self._last_update = 0
|
self._last_update = 0
|
||||||
|
@ -308,10 +308,11 @@ class Module(core.module.Module):
|
||||||
|
|
||||||
while newspaper_items:
|
while newspaper_items:
|
||||||
content += self._create_news_section(newspaper_items)
|
content += self._create_news_section(newspaper_items)
|
||||||
open(self._newspaper_filename, "w").write(
|
self._newspaper_file.write(
|
||||||
HTML_TEMPLATE.replace("[[CONTENT]]", content)
|
HTML_TEMPLATE.replace("[[CONTENT]]", content)
|
||||||
)
|
)
|
||||||
webbrowser.open("file://" + self._newspaper_filename)
|
self._newspaper_file.flush()
|
||||||
|
webbrowser.open("file://" + self._newspaper_file.name)
|
||||||
self._update_history("newspaper")
|
self._update_history("newspaper")
|
||||||
self._save_history()
|
self._save_history()
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,11 @@ class Module(core.module.Module):
|
||||||
self.__sun = None
|
self.__sun = None
|
||||||
|
|
||||||
if not lat or not lon:
|
if not lat or not lon:
|
||||||
lat, lon = util.location.coordinates()
|
try:
|
||||||
|
lat, lon = util.location.coordinates()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
if lat and lon:
|
if lat and lon:
|
||||||
self.__sun = Sun(float(lat), float(lon))
|
self.__sun = Sun(float(lat), float(lon))
|
||||||
|
|
||||||
|
@ -55,6 +59,10 @@ class Module(core.module.Module):
|
||||||
return "n/a"
|
return "n/a"
|
||||||
|
|
||||||
def __calculate_times(self):
|
def __calculate_times(self):
|
||||||
|
if not self.__sun:
|
||||||
|
self.__sunset = self.__sunrise = None
|
||||||
|
return
|
||||||
|
|
||||||
self.__isup = False
|
self.__isup = False
|
||||||
|
|
||||||
order_matters = True
|
order_matters = True
|
||||||
|
|
|
@ -13,7 +13,9 @@ Parameters:
|
||||||
* nic.exclude: Comma-separated list of interface prefixes (supporting regular expressions) to exclude (defaults to 'lo,virbr,docker,vboxnet,veth,br,.*:avahi')
|
* nic.exclude: Comma-separated list of interface prefixes (supporting regular expressions) to exclude (defaults to 'lo,virbr,docker,vboxnet,veth,br,.*:avahi')
|
||||||
* nic.include: Comma-separated list of interfaces to include
|
* nic.include: Comma-separated list of interfaces to include
|
||||||
* nic.states: Comma-separated list of states to show (prefix with '^' to invert - i.e. ^down -> show all devices that are not in state down)
|
* nic.states: Comma-separated list of states to show (prefix with '^' to invert - i.e. ^down -> show all devices that are not in state down)
|
||||||
* nic.format: Format string (defaults to '{intf} {state} {ip} {ssid}')
|
* nic.format: Format string (defaults to '{intf} {state} {ip} {ssid} {strength}')
|
||||||
|
* nic.strength_warning: Integer to set the threshold for warning state (defaults to 50)
|
||||||
|
* nic.strength_critical: Integer to set the threshold for critical state (defaults to 30)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
@ -28,7 +30,7 @@ import util.format
|
||||||
|
|
||||||
|
|
||||||
class Module(core.module.Module):
|
class Module(core.module.Module):
|
||||||
@core.decorators.every(seconds=10)
|
@core.decorators.every(seconds=5)
|
||||||
def __init__(self, config, theme):
|
def __init__(self, config, theme):
|
||||||
widgets = []
|
widgets = []
|
||||||
super().__init__(config, theme, widgets)
|
super().__init__(config, theme, widgets)
|
||||||
|
@ -45,7 +47,15 @@ class Module(core.module.Module):
|
||||||
self._states["exclude"].append(state[1:])
|
self._states["exclude"].append(state[1:])
|
||||||
else:
|
else:
|
||||||
self._states["include"].append(state)
|
self._states["include"].append(state)
|
||||||
self._format = self.parameter("format", "{intf} {state} {ip} {ssid}")
|
self._format = self.parameter("format", "{intf} {state} {ip} {ssid} {strength}")
|
||||||
|
|
||||||
|
self._strength_threshold_critical = self.parameter("strength_critical", 30)
|
||||||
|
self._strength_threshold_warning = self.parameter("strength_warning", 50)
|
||||||
|
|
||||||
|
# Limits for the accepted dBm values of wifi strength
|
||||||
|
self.__strength_dbm_lower_bound = -110
|
||||||
|
self.__strength_dbm_upper_bound = -30
|
||||||
|
|
||||||
self.iw = shutil.which("iw")
|
self.iw = shutil.which("iw")
|
||||||
self._update_widgets(widgets)
|
self._update_widgets(widgets)
|
||||||
|
|
||||||
|
@ -64,6 +74,14 @@ class Module(core.module.Module):
|
||||||
iftype = "wireless" if self._iswlan(intf) else "wired"
|
iftype = "wireless" if self._iswlan(intf) else "wired"
|
||||||
iftype = "tunnel" if self._istunnel(intf) else iftype
|
iftype = "tunnel" if self._istunnel(intf) else iftype
|
||||||
|
|
||||||
|
# "strength" is none if interface type is not wlan
|
||||||
|
strength = widget.get("strength")
|
||||||
|
if self._iswlan(intf) and strength:
|
||||||
|
if strength < self._strength_threshold_critical:
|
||||||
|
states.append("critical")
|
||||||
|
elif strength < self._strength_threshold_warning:
|
||||||
|
states.append("warning")
|
||||||
|
|
||||||
states.append("{}-{}".format(iftype, widget.get("state")))
|
states.append("{}-{}".format(iftype, widget.get("state")))
|
||||||
|
|
||||||
return states
|
return states
|
||||||
|
@ -116,6 +134,9 @@ class Module(core.module.Module):
|
||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
strength_dbm = self.get_strength_dbm(intf)
|
||||||
|
strength_percent = self.convert_strength_dbm_percent(strength_dbm)
|
||||||
|
|
||||||
widget = self.widget(intf)
|
widget = self.widget(intf)
|
||||||
if not widget:
|
if not widget:
|
||||||
widget = self.add_widget(name=intf)
|
widget = self.add_widget(name=intf)
|
||||||
|
@ -126,12 +147,14 @@ class Module(core.module.Module):
|
||||||
ip=", ".join(addr),
|
ip=", ".join(addr),
|
||||||
intf=intf,
|
intf=intf,
|
||||||
state=state,
|
state=state,
|
||||||
|
strength=str(strength_percent) + "%" if strength_percent else "",
|
||||||
ssid=self.get_ssid(intf),
|
ssid=self.get_ssid(intf),
|
||||||
).split()
|
).split()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
widget.set("intf", intf)
|
widget.set("intf", intf)
|
||||||
widget.set("state", state)
|
widget.set("state", state)
|
||||||
|
widget.set("strength", strength_percent)
|
||||||
|
|
||||||
def get_ssid(self, intf):
|
def get_ssid(self, intf):
|
||||||
if not self._iswlan(intf) or self._istunnel(intf) or not self.iw:
|
if not self._iswlan(intf) or self._istunnel(intf) or not self.iw:
|
||||||
|
@ -145,5 +168,23 @@ class Module(core.module.Module):
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
def get_strength_dbm(self, intf):
|
||||||
|
if not self._iswlan(intf) or self._istunnel(intf) or not self.iw:
|
||||||
|
return None
|
||||||
|
|
||||||
|
with open("/proc/net/wireless", "r") as file:
|
||||||
|
for line in file:
|
||||||
|
if intf in line:
|
||||||
|
# Remove trailing . by slicing it off ;)
|
||||||
|
strength_dbm = line.split()[3][:-1]
|
||||||
|
return util.format.asint(strength_dbm,
|
||||||
|
minimum=self.__strength_dbm_lower_bound,
|
||||||
|
maximum=self.__strength_dbm_upper_bound)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def convert_strength_dbm_percent(self, signal):
|
||||||
|
return int(100 * ((signal + 100) / 70.0)) if signal else None
|
||||||
|
|
||||||
|
|
||||||
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|
||||||
|
|
|
@ -54,7 +54,7 @@ def get_redshift_value(module):
|
||||||
for line in res.split("\n"):
|
for line in res.split("\n"):
|
||||||
line = line.lower()
|
line = line.lower()
|
||||||
if "temperature" in line:
|
if "temperature" in line:
|
||||||
widget.set("temp", line.split(" ")[2])
|
widget.set("temp", line.split(" ")[2].upper())
|
||||||
if "period" in line:
|
if "period" in line:
|
||||||
state = line.split(" ")[1]
|
state = line.split(" ")[1]
|
||||||
if "day" in state:
|
if "day" in state:
|
||||||
|
|
|
@ -9,7 +9,7 @@ Parameters:
|
||||||
import core.module
|
import core.module
|
||||||
import core.widget
|
import core.widget
|
||||||
import core.decorators
|
import core.decorators
|
||||||
|
import core.input
|
||||||
|
|
||||||
class Module(core.module.Module):
|
class Module(core.module.Module):
|
||||||
@core.decorators.every(minutes=60)
|
@core.decorators.every(minutes=60)
|
||||||
|
@ -20,5 +20,8 @@ class Module(core.module.Module):
|
||||||
def text(self, _):
|
def text(self, _):
|
||||||
return self.__text
|
return self.__text
|
||||||
|
|
||||||
|
def update_text(self, event):
|
||||||
|
self.__text = core.input.button_name(event["button"])
|
||||||
|
|
||||||
|
|
||||||
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|
||||||
|
|
|
@ -59,11 +59,11 @@ def __load():
|
||||||
__next = time.time() + 60 * 30 # error - try again every 30m
|
__next = time.time() + 60 * 30 # error - try again every 30m
|
||||||
|
|
||||||
|
|
||||||
def __get(name, default=None):
|
def __get(name):
|
||||||
global __data
|
global __data
|
||||||
if not __data or __expired():
|
if not __data or __expired():
|
||||||
__load()
|
__load()
|
||||||
return __data.get(name, default)
|
return __data[name]
|
||||||
|
|
||||||
|
|
||||||
def reset():
|
def reset():
|
||||||
|
|
|
@ -26,6 +26,15 @@ class PublicIPTest(TestCase):
|
||||||
|
|
||||||
assert widget(module).full_text() == '5.12.220.2'
|
assert widget(module).full_text() == '5.12.220.2'
|
||||||
|
|
||||||
|
@mock.patch('util.location.public_ip')
|
||||||
|
def test_public_ip(self, public_ip_mock):
|
||||||
|
public_ip_mock.return_value = None
|
||||||
|
|
||||||
|
module = build_module()
|
||||||
|
module.update()
|
||||||
|
|
||||||
|
assert widget(module).full_text() == 'n/a'
|
||||||
|
|
||||||
@mock.patch('util.location.public_ip')
|
@mock.patch('util.location.public_ip')
|
||||||
def test_public_ip_with_exception(self, public_ip_mock):
|
def test_public_ip_with_exception(self, public_ip_mock):
|
||||||
public_ip_mock.side_effect = Exception
|
public_ip_mock.side_effect = Exception
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
"default-separators": false
|
"default-separators": false
|
||||||
},
|
},
|
||||||
"date": { "prefix": "" },
|
"date": { "prefix": "" },
|
||||||
|
"persian_date": { "prefix": "" },
|
||||||
"time": { "prefix": "" },
|
"time": { "prefix": "" },
|
||||||
"datetime": { "prefix": "" },
|
"datetime": { "prefix": "" },
|
||||||
"datetz": { "prefix": "" },
|
"datetz": { "prefix": "" },
|
||||||
|
|
54
themes/rose-pine.json
Normal file
54
themes/rose-pine.json
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
{
|
||||||
|
"icons": ["awesome-fonts"],
|
||||||
|
"defaults": {
|
||||||
|
"separator-block-width": 0,
|
||||||
|
"warning": {
|
||||||
|
"fg": "#232136",
|
||||||
|
"bg": "#f6c177"
|
||||||
|
},
|
||||||
|
"critical": {
|
||||||
|
"fg": "#232136",
|
||||||
|
"bg": "#eb6f92"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cycle": [
|
||||||
|
{ "fg": "#232136", "bg": "#ea9a97" },
|
||||||
|
{ "fg": "#e0def4", "bg": "#393552" }
|
||||||
|
],
|
||||||
|
"dnf": {
|
||||||
|
"good": {
|
||||||
|
"fg": "#232136",
|
||||||
|
"bg": "#9ccfd8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pacman": {
|
||||||
|
"good": {
|
||||||
|
"fg": "#232136",
|
||||||
|
"bg": "#9ccfd8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"battery": {
|
||||||
|
"charged": {
|
||||||
|
"fg": "#232136",
|
||||||
|
"bg": "#9ccfd8"
|
||||||
|
},
|
||||||
|
"AC": {
|
||||||
|
"fg": "#232136",
|
||||||
|
"bg": "#9ccfd8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pomodoro": {
|
||||||
|
"paused": {
|
||||||
|
"fg": "#232136",
|
||||||
|
"bg": "#f6c177"
|
||||||
|
},
|
||||||
|
"work": {
|
||||||
|
"fg": "#232136",
|
||||||
|
"bg": "#9ccfd8"
|
||||||
|
},
|
||||||
|
"break": {
|
||||||
|
"fg": "#232136",
|
||||||
|
"bg": "#c4a7e7"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
78
themes/zengarden-powerline-light.json
Normal file
78
themes/zengarden-powerline-light.json
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
{
|
||||||
|
"icons": [ "paxy97", "awesome-fonts" ],
|
||||||
|
"defaults": {
|
||||||
|
"warning": {
|
||||||
|
"fg": "#353839",
|
||||||
|
"bg": "#b38a32"
|
||||||
|
},
|
||||||
|
"critical": {
|
||||||
|
"fg": "#353839",
|
||||||
|
"bg": "#d94070"
|
||||||
|
},
|
||||||
|
"default-separators": false,
|
||||||
|
"separator-block-width": 0
|
||||||
|
},
|
||||||
|
"fg": "#353839",
|
||||||
|
"bg": "#c4b6a3",
|
||||||
|
"dnf": {
|
||||||
|
"good": {
|
||||||
|
"fg": "#353839",
|
||||||
|
"bg": "#378c5d"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"apt": {
|
||||||
|
"good": {
|
||||||
|
"fg": "#353839",
|
||||||
|
"bg": "#378c5d"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"battery": {
|
||||||
|
"charged": {
|
||||||
|
"fg": "#353839",
|
||||||
|
"bg": "#378c5d"
|
||||||
|
},
|
||||||
|
"AC": {
|
||||||
|
"fg": "#353839",
|
||||||
|
"bg": "#378c5d"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bluetooth": {
|
||||||
|
"ON": {
|
||||||
|
"fg": "#353839",
|
||||||
|
"bg": "#378c5d"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"git": {
|
||||||
|
"modified": { "bg": "#174572" },
|
||||||
|
"deleted": { "bg": "#ba1d58" },
|
||||||
|
"new": { "bg": "#967117" }
|
||||||
|
},
|
||||||
|
"pomodoro": {
|
||||||
|
"paused": {
|
||||||
|
"fg": "#353839",
|
||||||
|
"bg": "#d79921"
|
||||||
|
},
|
||||||
|
"work": {
|
||||||
|
"fg": "#353839",
|
||||||
|
"bg": "#378c5d"
|
||||||
|
},
|
||||||
|
"break": {
|
||||||
|
"fg": "#353839",
|
||||||
|
"bg": "#378c5d"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"keys": {
|
||||||
|
"Key.cmd": {
|
||||||
|
"bg": "#477ab7"
|
||||||
|
},
|
||||||
|
"Key.shift": {
|
||||||
|
"bg": "#b38a32"
|
||||||
|
},
|
||||||
|
"Key.ctrl": {
|
||||||
|
"bg": "#377c8b"
|
||||||
|
},
|
||||||
|
"Key.alt": {
|
||||||
|
"bg": "#e05b1f"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue