Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
ab25a1b6eb
@ -122,3 +122,23 @@ Also change your i3wm config to the following:
|
|||||||
position top
|
position top
|
||||||
workspace_buttons yes
|
workspace_buttons yes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Settings that require credentials can utilize the keyring module to keep sensitive information out of config files.
|
||||||
|
To take advantage of this feature, simply use the setting_util.py script to set the credentials for a module. Once this
|
||||||
|
is done you can add the module to your config without specifying the credentials, eg:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
# Use the default keyring to retrieve credentials. To determine which backend is the default on your system, run
|
||||||
|
# python -c 'import keyring; print(keyring.get_keyring())'
|
||||||
|
status.register('github')
|
||||||
|
|
||||||
|
If you don't want to use the default you can set a specific keyring like so:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
from keyring.backends.file import PlaintextKeyring
|
||||||
|
status.register('github', keyring_backend=PlaintextKeyring())
|
||||||
|
|
||||||
|
|
||||||
|
i3pystatus will locate and set the credentials during the module loading process. Currently supported credentals are "password", "email" and "username".
|
@ -14,6 +14,9 @@ tools for this which make this even easier:
|
|||||||
periodically.
|
periodically.
|
||||||
- Settings (already built into above classes) allow you to easily
|
- Settings (already built into above classes) allow you to easily
|
||||||
specify user-modifiable attributes of your class for configuration.
|
specify user-modifiable attributes of your class for configuration.
|
||||||
|
- For modules that require credentials, it is recommended to add a
|
||||||
|
keyring_backend setting to allow users to specify their own backends
|
||||||
|
for retrieving sensitive credentials.
|
||||||
|
|
||||||
Required settings and default values are also handled.
|
Required settings and default values are also handled.
|
||||||
|
|
||||||
|
@ -32,13 +32,24 @@ class Clock(IntervalModule):
|
|||||||
on_downscroll = ["scroll_format", -1]
|
on_downscroll = ["scroll_format", -1]
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
lang, enc = os.environ.get('LANG', None).split('.', 1)
|
env_lang = os.environ.get('LC_TIME', None)
|
||||||
if lang != locale.getlocale(locale.LC_TIME)[0]:
|
if env_lang is None:
|
||||||
|
env_lang = os.environ.get('LANG', None)
|
||||||
|
|
||||||
|
if env_lang is not None:
|
||||||
|
if env_lang.find('.') != -1:
|
||||||
|
lang = tuple(env_lang.split('.', 1))
|
||||||
|
else:
|
||||||
|
lang = (env_lang, None)
|
||||||
|
else:
|
||||||
|
lang = (None, None)
|
||||||
|
|
||||||
|
if lang != locale.getlocale(locale.LC_TIME):
|
||||||
# affects datetime.time.strftime() in whole program
|
# affects datetime.time.strftime() in whole program
|
||||||
locale.setlocale(locale.LC_TIME, (lang, enc))
|
locale.setlocale(locale.LC_TIME, lang)
|
||||||
|
|
||||||
if self.format is None:
|
if self.format is None:
|
||||||
if lang == 'en_US':
|
if lang[0] == 'en_US':
|
||||||
# MDY format - United States of America
|
# MDY format - United States of America
|
||||||
self.format = ["%a %b %-d %X"]
|
self.format = ["%a %b %-d %X"]
|
||||||
else:
|
else:
|
||||||
|
@ -2,10 +2,10 @@ from i3pystatus.core.util import KeyConstraintDict
|
|||||||
from i3pystatus.core.exceptions import ConfigKeyError, ConfigMissingError
|
from i3pystatus.core.exceptions import ConfigKeyError, ConfigMissingError
|
||||||
import inspect
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
|
import getpass
|
||||||
|
|
||||||
|
|
||||||
class SettingsBase:
|
class SettingsBase:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Support class for providing a nice and flexible settings interface
|
Support class for providing a nice and flexible settings interface
|
||||||
|
|
||||||
@ -18,6 +18,8 @@ class SettingsBase:
|
|||||||
Settings are stored as attributes of self.
|
Settings are stored as attributes of self.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
__PROTECTED_SETTINGS = ["password", "email", "username"]
|
||||||
|
|
||||||
settings = (
|
settings = (
|
||||||
("log_level", "Set to true to log error to .i3pystatus-<pid> file"),
|
("log_level", "Set to true to log error to .i3pystatus-<pid> file"),
|
||||||
)
|
)
|
||||||
@ -44,21 +46,25 @@ class SettingsBase:
|
|||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
def merge_with_parents_settings():
|
def merge_with_parents_settings():
|
||||||
|
|
||||||
settings = tuple()
|
settings = tuple()
|
||||||
|
|
||||||
# getmro returns base classes according to Method Resolution Order
|
# getmro returns base classes according to Method Resolution Order
|
||||||
for cls in inspect.getmro(self.__class__):
|
for cls in inspect.getmro(self.__class__):
|
||||||
if hasattr(cls, "settings"):
|
if hasattr(cls, "settings"):
|
||||||
settings = settings + cls.settings
|
settings = settings + cls.settings
|
||||||
return settings
|
return settings
|
||||||
|
|
||||||
|
self.__name__ = "{}.{}".format(
|
||||||
|
self.__module__, self.__class__.__name__)
|
||||||
|
|
||||||
settings = merge_with_parents_settings()
|
settings = merge_with_parents_settings()
|
||||||
settings = self.flatten_settings(settings)
|
settings = self.flatten_settings(settings)
|
||||||
|
|
||||||
sm = KeyConstraintDict(settings, self.required)
|
sm = KeyConstraintDict(settings, self.required)
|
||||||
settings_source = get_argument_dict(args, kwargs)
|
settings_source = get_argument_dict(args, kwargs)
|
||||||
|
|
||||||
|
protected = self.get_protected_settings(settings_source)
|
||||||
|
settings_source.update(protected)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
sm.update(settings_source)
|
sm.update(settings_source)
|
||||||
except KeyError as exc:
|
except KeyError as exc:
|
||||||
@ -70,13 +76,48 @@ class SettingsBase:
|
|||||||
raise ConfigMissingError(
|
raise ConfigMissingError(
|
||||||
type(self).__name__, missing=exc.keys) from exc
|
type(self).__name__, missing=exc.keys) from exc
|
||||||
|
|
||||||
self.__name__ = "{}.{}".format(
|
|
||||||
self.__module__, self.__class__.__name__)
|
|
||||||
|
|
||||||
self.logger = logging.getLogger(self.__name__)
|
self.logger = logging.getLogger(self.__name__)
|
||||||
self.logger.setLevel(self.log_level)
|
self.logger.setLevel(self.log_level)
|
||||||
self.init()
|
self.init()
|
||||||
|
|
||||||
|
def get_protected_settings(self, settings_source):
|
||||||
|
"""
|
||||||
|
Attempt to retrieve protected settings from keyring if they are not already set.
|
||||||
|
"""
|
||||||
|
user_backend = settings_source.get('keyring_backend')
|
||||||
|
found_settings = dict()
|
||||||
|
for setting_name in self.__PROTECTED_SETTINGS:
|
||||||
|
# Nothing to do if the setting is already defined.
|
||||||
|
if settings_source.get(setting_name):
|
||||||
|
continue
|
||||||
|
|
||||||
|
setting = None
|
||||||
|
identifier = "%s.%s" % (self.__name__, setting_name)
|
||||||
|
if hasattr(self, 'required') and setting_name in getattr(self, 'required'):
|
||||||
|
setting = self.get_setting_from_keyring(identifier, user_backend)
|
||||||
|
elif hasattr(self, setting_name):
|
||||||
|
setting = self.get_setting_from_keyring(identifier, user_backend)
|
||||||
|
if setting:
|
||||||
|
found_settings.update({setting_name: setting})
|
||||||
|
return found_settings
|
||||||
|
|
||||||
|
def get_setting_from_keyring(self, setting_identifier, keyring_backend=None):
|
||||||
|
"""
|
||||||
|
Retrieves a protected setting from keyring
|
||||||
|
:param setting_identifier: must be in the format package.module.Class.setting
|
||||||
|
"""
|
||||||
|
# If a custom keyring backend has been defined, use it.
|
||||||
|
if keyring_backend:
|
||||||
|
return keyring_backend.get_password(setting_identifier, getpass.getuser())
|
||||||
|
|
||||||
|
# Otherwise try and use default keyring.
|
||||||
|
try:
|
||||||
|
import keyring
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return keyring.get_password(setting_identifier, getpass.getuser())
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
"""Convenience method which is called after all settings are set
|
"""Convenience method which is called after all settings are set
|
||||||
|
|
||||||
|
@ -383,7 +383,7 @@ def make_graph(values, lower_limit=0.0, upper_limit=100.0, style="blocks"):
|
|||||||
extent = mx - mn
|
extent = mx - mn
|
||||||
|
|
||||||
if style == 'blocks':
|
if style == 'blocks':
|
||||||
bar = u'_▁▂▃▄▅▆▇█'
|
bar = '_▁▂▃▄▅▆▇█'
|
||||||
bar_count = len(bar) - 1
|
bar_count = len(bar) - 1
|
||||||
if extent == 0:
|
if extent == 0:
|
||||||
graph = '_' * len(values)
|
graph = '_' * len(values)
|
||||||
@ -436,7 +436,7 @@ def make_vertical_bar(percentage, width=1):
|
|||||||
:param width: How many characters wide the bar should be.
|
:param width: How many characters wide the bar should be.
|
||||||
:returns: Bar as a String
|
:returns: Bar as a String
|
||||||
"""
|
"""
|
||||||
bar = u' _▁▂▃▄▅▆▇█'
|
bar = ' _▁▂▃▄▅▆▇█'
|
||||||
percentage //= 10
|
percentage //= 10
|
||||||
if percentage < 0:
|
if percentage < 0:
|
||||||
output = bar[0]
|
output = bar[0]
|
||||||
|
@ -29,6 +29,7 @@ class CpuUsage(IntervalModule):
|
|||||||
format = "{usage:02}%"
|
format = "{usage:02}%"
|
||||||
format_all = "{core}:{usage:02}%"
|
format_all = "{core}:{usage:02}%"
|
||||||
exclude_average = False
|
exclude_average = False
|
||||||
|
interval = 1
|
||||||
settings = (
|
settings = (
|
||||||
("format", "format string."),
|
("format", "format string."),
|
||||||
("format_all", ("format string used for {usage_all} per core. "
|
("format_all", ("format string used for {usage_all} per core. "
|
||||||
@ -40,7 +41,6 @@ class CpuUsage(IntervalModule):
|
|||||||
def init(self):
|
def init(self):
|
||||||
self.prev_total = defaultdict(int)
|
self.prev_total = defaultdict(int)
|
||||||
self.prev_busy = defaultdict(int)
|
self.prev_busy = defaultdict(int)
|
||||||
self.interval = 1
|
|
||||||
self.formatter = Formatter()
|
self.formatter = Formatter()
|
||||||
|
|
||||||
def get_cpu_timings(self):
|
def get_cpu_timings(self):
|
||||||
|
@ -38,6 +38,8 @@ class Disk(IntervalModule):
|
|||||||
self.output = {}
|
self.output = {}
|
||||||
return
|
return
|
||||||
|
|
||||||
|
critical = available < self.critical_limit
|
||||||
|
|
||||||
cdict = {
|
cdict = {
|
||||||
"total": (stat.f_bsize * stat.f_blocks) / self.divisor,
|
"total": (stat.f_bsize * stat.f_blocks) / self.divisor,
|
||||||
"free": (stat.f_bsize * stat.f_bfree) / self.divisor,
|
"free": (stat.f_bsize * stat.f_bfree) / self.divisor,
|
||||||
@ -51,6 +53,6 @@ class Disk(IntervalModule):
|
|||||||
|
|
||||||
self.output = {
|
self.output = {
|
||||||
"full_text": self.format.format(**cdict),
|
"full_text": self.format.format(**cdict),
|
||||||
"color": self.color if available > self.critical_limit else self.critical_color,
|
"color": self.critical_color if critical else self.color,
|
||||||
"urgent": available > self.critical_limit
|
"urgent": critical
|
||||||
}
|
}
|
||||||
|
@ -16,18 +16,20 @@ class Github(IntervalModule):
|
|||||||
* `{unread_count}` - number of unread notifications, empty if 0
|
* `{unread_count}` - number of unread notifications, empty if 0
|
||||||
"""
|
"""
|
||||||
|
|
||||||
unread_marker = u"●"
|
unread_marker = "●"
|
||||||
unread = ''
|
unread = ''
|
||||||
color = '#78EAF2'
|
color = '#78EAF2'
|
||||||
username = ''
|
username = ''
|
||||||
password = ''
|
password = ''
|
||||||
format = '{unread}'
|
format = '{unread}'
|
||||||
interval = 600
|
interval = 600
|
||||||
|
keyring_backend = None
|
||||||
|
|
||||||
on_leftclick = 'open_github'
|
on_leftclick = 'open_github'
|
||||||
|
|
||||||
settings = (
|
settings = (
|
||||||
('format', 'format string'),
|
('format', 'format string'),
|
||||||
|
('keyring_backend', 'alternative keyring backend for retrieving credentials'),
|
||||||
('unread_marker', 'sets the string that the "unread" formatter shows when there are pending notifications'),
|
('unread_marker', 'sets the string that the "unread" formatter shows when there are pending notifications'),
|
||||||
("username", ""),
|
("username", ""),
|
||||||
("password", ""),
|
("password", ""),
|
||||||
|
@ -16,10 +16,12 @@ class IMAP(Backend):
|
|||||||
settings = (
|
settings = (
|
||||||
"host", "port",
|
"host", "port",
|
||||||
"username", "password",
|
"username", "password",
|
||||||
|
('keyring_backend', 'alternative keyring backend for retrieving credentials'),
|
||||||
"ssl",
|
"ssl",
|
||||||
"mailbox",
|
"mailbox",
|
||||||
)
|
)
|
||||||
required = ("host", "username", "password")
|
required = ("host", "username", "password")
|
||||||
|
keyring_backend = None
|
||||||
|
|
||||||
port = 993
|
port = 993
|
||||||
ssl = True
|
ssl = True
|
||||||
|
@ -20,10 +20,12 @@ class ModsDeChecker(IntervalModule):
|
|||||||
settings = (
|
settings = (
|
||||||
("format",
|
("format",
|
||||||
"""Use {unread} as the formatter for number of unread posts"""),
|
"""Use {unread} as the formatter for number of unread posts"""),
|
||||||
|
('keyring_backend', 'alternative keyring backend for retrieving credentials'),
|
||||||
("offset", """subtract number of posts before output"""),
|
("offset", """subtract number of posts before output"""),
|
||||||
"color", "username", "password"
|
"color", "username", "password"
|
||||||
)
|
)
|
||||||
required = ("username", "password")
|
required = ("username", "password")
|
||||||
|
keyring_backend = None
|
||||||
|
|
||||||
color = "#7181fe"
|
color = "#7181fe"
|
||||||
offset = 0
|
offset = 0
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import socket
|
import socket
|
||||||
from os.path import basename
|
from os.path import basename
|
||||||
|
from math import floor
|
||||||
|
|
||||||
from i3pystatus import IntervalModule, formatp
|
from i3pystatus import IntervalModule, formatp
|
||||||
from i3pystatus.core.util import TimeWrapper
|
from i3pystatus.core.util import TimeWrapper
|
||||||
@ -34,8 +35,10 @@ class MPD(IntervalModule):
|
|||||||
("format", "formatp string"),
|
("format", "formatp string"),
|
||||||
("status", "Dictionary mapping pause, play and stop to output"),
|
("status", "Dictionary mapping pause, play and stop to output"),
|
||||||
("color", "The color of the text"),
|
("color", "The color of the text"),
|
||||||
("text_len", "Defines max length for title, album and artist, if truncated ellipsis are appended as indicator"),
|
("max_field_len", "Defines max length for in truncate_fields defined fields, if truncated, ellipsis are appended as indicator. It's applied *before* max_len. Value of 0 disables this."),
|
||||||
("truncate_fields", "fileds that will be truncated if exceeding text_len"),
|
("max_len", "Defines max length for the hole string, if exceeding fields specefied in truncate_fields are truncated equaly. If truncated, ellipsis are appended as indicator. It's applied *after* max_field_len. Value of 0 disables this."),
|
||||||
|
("truncate_fields", "fields that will be truncated if exceeding max_field_len or max_len."),
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
host = "localhost"
|
host = "localhost"
|
||||||
@ -48,7 +51,8 @@ class MPD(IntervalModule):
|
|||||||
"stop": "◾",
|
"stop": "◾",
|
||||||
}
|
}
|
||||||
color = "#FFFFFF"
|
color = "#FFFFFF"
|
||||||
text_len = 25
|
max_field_len = 25
|
||||||
|
max_len = 100
|
||||||
truncate_fields = ("title", "album", "artist")
|
truncate_fields = ("title", "album", "artist")
|
||||||
on_leftclick = "switch_playpause"
|
on_leftclick = "switch_playpause"
|
||||||
on_rightclick = "next_song"
|
on_rightclick = "next_song"
|
||||||
@ -91,17 +95,30 @@ class MPD(IntervalModule):
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for key in self.truncate_fields:
|
|
||||||
if len(fdict[key]) > self.text_len:
|
|
||||||
fdict[key] = fdict[key][:self.text_len - 1] + "…"
|
|
||||||
|
|
||||||
if not fdict["title"] and "filename" in fdict:
|
if not fdict["title"] and "filename" in fdict:
|
||||||
fdict["filename"] = '.'.join(
|
fdict["filename"] = '.'.join(
|
||||||
basename(currentsong["file"]).split('.')[:-1])
|
basename(currentsong["file"]).split('.')[:-1])
|
||||||
else:
|
else:
|
||||||
fdict["filename"] = ""
|
fdict["filename"] = ""
|
||||||
|
|
||||||
|
if self.max_field_len > 0:
|
||||||
|
for key in self.truncate_fields:
|
||||||
|
if len(fdict[key]) > self.max_field_len:
|
||||||
|
fdict[key] = fdict[key][:self.max_field_len - 1] + "…"
|
||||||
|
|
||||||
|
full_text = formatp(self.format, **fdict).strip()
|
||||||
|
full_text_len = len(full_text)
|
||||||
|
if full_text_len > self.max_len and self.max_len > 0:
|
||||||
|
shrink = floor((self.max_len - full_text_len) /
|
||||||
|
len(self.truncate_fields)) - 1
|
||||||
|
|
||||||
|
for key in self.truncate_fields:
|
||||||
|
fdict[key] = fdict[key][:shrink] + "…"
|
||||||
|
|
||||||
|
full_text = formatp(self.format, **fdict).strip()
|
||||||
|
|
||||||
self.output = {
|
self.output = {
|
||||||
"full_text": formatp(self.format, **fdict).strip(),
|
"full_text": full_text,
|
||||||
"color": self.color,
|
"color": self.color,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,7 +338,6 @@ class Network(IntervalModule, ColorRangeModule):
|
|||||||
format_values = dict(kbs="", network_graph="", bytes_sent="", bytes_recv="", packets_sent="", packets_recv="",
|
format_values = dict(kbs="", network_graph="", bytes_sent="", bytes_recv="", packets_sent="", packets_recv="",
|
||||||
interface="", v4="", v4mask="", v4cidr="", v6="", v6mask="", v6cidr="", mac="",
|
interface="", v4="", v4mask="", v4cidr="", v6="", v6mask="", v6cidr="", mac="",
|
||||||
essid="", freq="", quality="", quality_bar="")
|
essid="", freq="", quality="", quality_bar="")
|
||||||
color = None
|
|
||||||
if self.network_traffic:
|
if self.network_traffic:
|
||||||
network_usage = self.network_traffic.get_usage(self.interface)
|
network_usage = self.network_traffic.get_usage(self.interface)
|
||||||
format_values.update(network_usage)
|
format_values.update(network_usage)
|
||||||
@ -352,22 +351,22 @@ class Network(IntervalModule, ColorRangeModule):
|
|||||||
format_values['network_graph'] = self.get_network_graph(kbs)
|
format_values['network_graph'] = self.get_network_graph(kbs)
|
||||||
format_values['kbs'] = "{0:.1f}".format(round(kbs, 2)).rjust(6)
|
format_values['kbs'] = "{0:.1f}".format(round(kbs, 2)).rjust(6)
|
||||||
color = self.get_gradient(kbs, self.colors, self.upper_limit)
|
color = self.get_gradient(kbs, self.colors, self.upper_limit)
|
||||||
|
else:
|
||||||
|
color = None
|
||||||
|
|
||||||
|
if sysfs_interface_up(self.interface, self.unknown_up):
|
||||||
|
if not color:
|
||||||
|
color = self.color_up
|
||||||
|
format_str = self.format_up
|
||||||
|
else:
|
||||||
|
color = self.color_down
|
||||||
|
format_str = self.format_down
|
||||||
|
|
||||||
network_info = self.network_info.get_info(self.interface)
|
network_info = self.network_info.get_info(self.interface)
|
||||||
format_values.update(network_info)
|
format_values.update(network_info)
|
||||||
|
|
||||||
format_values['interface'] = self.interface
|
format_values['interface'] = self.interface
|
||||||
if sysfs_interface_up(self.interface, self.unknown_up):
|
|
||||||
if not self.dynamic_color:
|
|
||||||
color = self.color_up
|
|
||||||
|
|
||||||
self.output = {
|
self.output = {
|
||||||
"full_text": self.format_up.format(**format_values),
|
"full_text": format_str.format(**format_values),
|
||||||
'color': color,
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
color = self.color_down
|
|
||||||
self.output = {
|
|
||||||
"full_text": self.format_down.format(**format_values),
|
|
||||||
'color': color,
|
'color': color,
|
||||||
}
|
}
|
||||||
|
@ -80,6 +80,10 @@ class NowPlaying(IntervalModule):
|
|||||||
def get_player(self):
|
def get_player(self):
|
||||||
if self.player:
|
if self.player:
|
||||||
player = "org.mpris.MediaPlayer2." + self.player
|
player = "org.mpris.MediaPlayer2." + self.player
|
||||||
|
try:
|
||||||
|
return dbus.SessionBus().get_object(player, "/org/mpris/MediaPlayer2")
|
||||||
|
except dbus.exceptions.DBusException:
|
||||||
|
raise NoPlayerException()
|
||||||
else:
|
else:
|
||||||
player = self.find_player()
|
player = self.find_player()
|
||||||
return dbus.SessionBus().get_object(player, "/org/mpris/MediaPlayer2")
|
return dbus.SessionBus().get_object(player, "/org/mpris/MediaPlayer2")
|
||||||
|
@ -29,9 +29,11 @@ class pyLoad(IntervalModule):
|
|||||||
"format",
|
"format",
|
||||||
"captcha_true", "captcha_false",
|
"captcha_true", "captcha_false",
|
||||||
"download_true", "download_false",
|
"download_true", "download_false",
|
||||||
"username", "password"
|
"username", "password",
|
||||||
|
('keyring_backend', 'alternative keyring backend for retrieving credentials'),
|
||||||
)
|
)
|
||||||
required = ("username", "password")
|
required = ("username", "password")
|
||||||
|
keyring_backend = None
|
||||||
|
|
||||||
address = "http://127.0.0.1:8000"
|
address = "http://127.0.0.1:8000"
|
||||||
format = "{captcha} {progress_all:.1f}% {speed:.1f} kb/s"
|
format = "{captcha} {progress_all:.1f}% {speed:.1f} kb/s"
|
||||||
|
@ -35,6 +35,7 @@ class Reddit(IntervalModule):
|
|||||||
("format", "Format string used for output."),
|
("format", "Format string used for output."),
|
||||||
("username", "Reddit username."),
|
("username", "Reddit username."),
|
||||||
("password", "Reddit password."),
|
("password", "Reddit password."),
|
||||||
|
('keyring_backend', 'alternative keyring backend for retrieving credentials'),
|
||||||
("subreddit", "Subreddit to monitor. Uses frontpage if unspecified."),
|
("subreddit", "Subreddit to monitor. Uses frontpage if unspecified."),
|
||||||
("sort_by", "'hot', 'new', 'rising', 'controversial', or 'top'."),
|
("sort_by", "'hot', 'new', 'rising', 'controversial', or 'top'."),
|
||||||
("color", "Standard color."),
|
("color", "Standard color."),
|
||||||
@ -48,6 +49,7 @@ class Reddit(IntervalModule):
|
|||||||
format = "[{submission_subreddit}] {submission_title} ({submission_domain})"
|
format = "[{submission_subreddit}] {submission_title} ({submission_domain})"
|
||||||
username = ""
|
username = ""
|
||||||
password = ""
|
password = ""
|
||||||
|
keyring_backend = None
|
||||||
subreddit = ""
|
subreddit = ""
|
||||||
sort_by = "hot"
|
sort_by = "hot"
|
||||||
color = "#FFFFFF"
|
color = "#FFFFFF"
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
import re
|
|
||||||
import glob
|
|
||||||
|
|
||||||
from i3pystatus import IntervalModule
|
from i3pystatus import IntervalModule
|
||||||
|
|
||||||
|
|
||||||
@ -16,10 +13,14 @@ class Temperature(IntervalModule):
|
|||||||
"format string used for output. {temp} is the temperature in degrees celsius"),
|
"format string used for output. {temp} is the temperature in degrees celsius"),
|
||||||
"color",
|
"color",
|
||||||
"file",
|
"file",
|
||||||
|
"alert_temp",
|
||||||
|
"alert_color",
|
||||||
)
|
)
|
||||||
format = "{temp} °C"
|
format = "{temp} °C"
|
||||||
color = "#FFFFFF"
|
color = "#FFFFFF"
|
||||||
file = "/sys/class/thermal/thermal_zone0/temp"
|
file = "/sys/class/thermal/thermal_zone0/temp"
|
||||||
|
alert_temp = 90
|
||||||
|
alert_color = "#FF0000"
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
with open(self.file, "r") as f:
|
with open(self.file, "r") as f:
|
||||||
@ -27,5 +28,5 @@ class Temperature(IntervalModule):
|
|||||||
|
|
||||||
self.output = {
|
self.output = {
|
||||||
"full_text": self.format.format(temp=temp),
|
"full_text": self.format.format(temp=temp),
|
||||||
"color": self.color,
|
"color": self.color if temp < self.alert_temp else self.alert_color,
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ from bs4 import BeautifulSoup
|
|||||||
|
|
||||||
|
|
||||||
class WhosOnLocation():
|
class WhosOnLocation():
|
||||||
|
|
||||||
email = None
|
email = None
|
||||||
password = None
|
password = None
|
||||||
session = None
|
session = None
|
||||||
@ -68,9 +67,11 @@ class WOL(IntervalModule):
|
|||||||
password = None
|
password = None
|
||||||
|
|
||||||
settings = (
|
settings = (
|
||||||
|
('keyring_backend', 'alternative keyring backend for retrieving credentials'),
|
||||||
'email',
|
'email',
|
||||||
'password'
|
'password'
|
||||||
)
|
)
|
||||||
|
keyring_backend = None
|
||||||
|
|
||||||
color_on_site = '#00FF00'
|
color_on_site = '#00FF00'
|
||||||
color_off_site = '#ff0000'
|
color_off_site = '#ff0000'
|
||||||
|
80
setting_util.py
Executable file
80
setting_util.py
Executable file
@ -0,0 +1,80 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
import glob
|
||||||
|
import inspect
|
||||||
|
import os
|
||||||
|
import keyring
|
||||||
|
import getpass
|
||||||
|
import sys
|
||||||
|
import signal
|
||||||
|
from i3pystatus import Module, SettingsBase
|
||||||
|
from i3pystatus.core import ClassFinder
|
||||||
|
from collections import defaultdict, OrderedDict
|
||||||
|
|
||||||
|
def signal_handler(signal, frame):
|
||||||
|
sys.exit(0)
|
||||||
|
signal.signal(signal.SIGINT, signal_handler)
|
||||||
|
|
||||||
|
|
||||||
|
def get_int_in_range(prompt, _range):
|
||||||
|
while True:
|
||||||
|
answer = input(prompt)
|
||||||
|
try:
|
||||||
|
n = int(answer.strip())
|
||||||
|
if n in _range:
|
||||||
|
return n
|
||||||
|
else:
|
||||||
|
print("Value out of range!")
|
||||||
|
except ValueError:
|
||||||
|
print("Invalid input!")
|
||||||
|
|
||||||
|
modules = [os.path.basename(m.replace('.py', ''))
|
||||||
|
for m in glob.glob(os.path.join(os.path.dirname(__file__), "i3pystatus", "*.py"))
|
||||||
|
if not os.path.basename(m).startswith('_')]
|
||||||
|
|
||||||
|
protected_settings = SettingsBase._SettingsBase__PROTECTED_SETTINGS
|
||||||
|
class_finder = ClassFinder(Module)
|
||||||
|
credential_modules = defaultdict(dict)
|
||||||
|
for module_name in modules:
|
||||||
|
try:
|
||||||
|
module = class_finder.get_module(module_name)
|
||||||
|
clazz = class_finder.get_class(module)
|
||||||
|
members = [m[0] for m in inspect.getmembers(clazz) if not m[0].startswith('_')]
|
||||||
|
if any([hasattr(clazz, setting) for setting in protected_settings]):
|
||||||
|
credential_modules[clazz.__name__]['credentials'] = list(set(protected_settings) & set(members))
|
||||||
|
credential_modules[clazz.__name__]['key'] = "%s.%s" % (clazz.__module__, clazz.__name__)
|
||||||
|
elif hasattr(clazz, 'required'):
|
||||||
|
protected = []
|
||||||
|
required = getattr(clazz, 'required')
|
||||||
|
for setting in protected_settings:
|
||||||
|
if setting in required:
|
||||||
|
protected.append(setting)
|
||||||
|
if protected:
|
||||||
|
credential_modules[clazz.__name__]['credentials'] = protected
|
||||||
|
credential_modules[clazz.__name__]['key'] = "%s.%s" % (clazz.__module__, clazz.__name__)
|
||||||
|
|
||||||
|
except ImportError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
choices = [k for k in credential_modules.keys()]
|
||||||
|
for idx, module in enumerate(choices, start=1):
|
||||||
|
print("%s - %s" % (idx, module))
|
||||||
|
|
||||||
|
index = get_int_in_range("Choose module:\n> ", range(1, len(choices) + 1))
|
||||||
|
module_name = choices[index - 1]
|
||||||
|
module = credential_modules[module_name]
|
||||||
|
|
||||||
|
for idx, setting in enumerate(module['credentials'], start=1):
|
||||||
|
print("%s - %s" % (idx, setting))
|
||||||
|
|
||||||
|
choices = module['credentials']
|
||||||
|
index = get_int_in_range("Choose setting for %s:\n> " % module_name, range(1, len(choices) + 1))
|
||||||
|
setting = choices[index - 1]
|
||||||
|
|
||||||
|
answer = getpass.getpass("Enter value for %s:\n> " % setting)
|
||||||
|
answer2 = getpass.getpass("Re-enter value\n> ")
|
||||||
|
if answer == answer2:
|
||||||
|
key = "%s.%s" % (module['key'], setting)
|
||||||
|
keyring.set_password(key, getpass.getuser(), answer)
|
||||||
|
print("%s set!" % setting)
|
||||||
|
else:
|
||||||
|
print("Values don't match - nothing set.")
|
Loading…
Reference in New Issue
Block a user