summaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/battery-level.py68
-rw-r--r--modules/keyboard-layout.py73
-rw-r--r--modules/mpd_status.py117
-rw-r--r--modules/scratchpad-counter.py50
-rw-r--r--modules/window-title.py58
5 files changed, 366 insertions, 0 deletions
diff --git a/modules/battery-level.py b/modules/battery-level.py
new file mode 100644
index 0000000..6c30556
--- /dev/null
+++ b/modules/battery-level.py
@@ -0,0 +1,68 @@
+# -*- coding: utf8 -*-
+
+from __future__ import division # python2 compatibility
+from time import time
+
+import math
+import re
+import subprocess
+
+"""
+Module for displaying information about battery.
+
+Requires:
+ - the 'acpi' command line
+
+@author shadowprince
+@license Eclipse Public License
+"""
+
+CACHE_TIMEOUT = 30 # time to update battery
+HIDE_WHEN_FULL = False # hide any information when battery is fully charged
+
+MODE = "bar" # for primitive-one-char bar, or "text" for text percentage ouput
+
+BLOCKS = ["_", "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"] # block for bar
+TEXT_FORMAT = "Battery: {}" # text with "text" mode. percentage with % replaces {}
+
+CHARGING_CHARACTER = "⚡"
+
+# None means - get it from i3 config
+COLOR_BAD = None
+COLOR_CHARGING = "#FCE94F"
+COLOR_DEGRADED = None
+COLOR_GOOD = None
+
+
+class Py3status:
+ def battery_level(self, i3status_output_json, i3status_config):
+ response = {'name': 'battery-level'}
+
+ acpi = subprocess.check_output(["acpi"]).decode('utf-8')
+ proc = int(re.search(r"(\d+)%", acpi).group(1))
+
+ charging = bool(re.match(r".*Charging.*", acpi))
+ full = bool(re.match(r".*Unknown.*", acpi)) or bool(re.match(r".*Full.*", acpi))
+
+ if MODE == "bar":
+ character = BLOCKS[int(math.ceil(proc/100*(len(BLOCKS) - 1)))]
+ else:
+ character = TEXT_FORMAT.format(str(proc) + "%")
+
+ if proc < 30:
+ response['color'] = COLOR_DEGRADED if COLOR_DEGRADED else i3status_config['color_degraded']
+ if proc < 10:
+ response['color'] = COLOR_BAD if COLOR_BAD else i3status_config['color_bad']
+
+ if full:
+ response['color'] = COLOR_GOOD if COLOR_GOOD else i3status_config['color_good']
+ response['full_text'] = "" if HIDE_WHEN_FULL else BLOCKS[-1]
+ elif charging:
+ response['color'] = COLOR_CHARGING
+ response['full_text'] = CHARGING_CHARACTER
+ else:
+ response['full_text'] = character
+
+ response['cached_until'] = time() + CACHE_TIMEOUT
+
+ return (0, response)
diff --git a/modules/keyboard-layout.py b/modules/keyboard-layout.py
new file mode 100644
index 0000000..1ed3cee
--- /dev/null
+++ b/modules/keyboard-layout.py
@@ -0,0 +1,73 @@
+from subprocess import check_output
+from time import time
+
+"""
+Module for showing current keyboard layout.
+
+Requires:
+ - xkblayout-state
+ or
+ - setxkbmap
+
+@author shadowprince
+@license Eclipse Public License
+"""
+
+CACHE_TIMEOUT = 10 # refresh time to update indicator
+
+# colors of layouts, check your command's output to match keys
+LANG_COLORS = {
+ 'fr': '#268BD2', # solarized blue
+ 'ru': '#F75252', # red
+ 'ua': '#FCE94F', # yellow
+ 'us': '#729FCF', # light blue
+}
+
+
+def xbklayout():
+ """
+ check using xkblayout-state (preferred method)
+ """
+ return check_output(
+ ["xkblayout-state", "print", "%s"]
+ ).decode('utf-8')
+
+
+def setxkbmap():
+ """
+ check using setxkbmap >= 1.3.0
+
+ Please read issue 33 for more information :
+ https://github.com/ultrabug/py3status/pull/33
+ """
+ q = check_output(['setxkbmap', '-query']).decode('utf-8')
+ return q.replace(' ', '').split(':')[-1]
+
+
+class Py3status:
+ def __init__(self):
+ """
+ find the best implementation to get the keyboard's layout
+ """
+ try:
+ xbklayout()
+ except:
+ self.command = setxkbmap
+ else:
+ self.command = xbklayout
+
+ def keyboard_layout(self, i3status_output_json, i3status_config):
+ response = {
+ 'full_text': '',
+ 'name': 'keyboard-layout',
+ 'cached_until': time() + CACHE_TIMEOUT
+ }
+
+ lang = self.command().strip()
+ lang_color = LANG_COLORS.get(lang)
+
+ response['full_text'] = lang or '??'
+ if lang_color:
+ response['color'] = lang_color
+
+ return (0, response)
diff --git a/modules/mpd_status.py b/modules/mpd_status.py
new file mode 100644
index 0000000..28e99dc
--- /dev/null
+++ b/modules/mpd_status.py
@@ -0,0 +1,117 @@
+# -*- coding: utf-8 -*-
+
+import re
+
+from mpd import (MPDClient, CommandError)
+from socket import error as SocketError
+from time import time
+
+"""
+Mpd - module to show information from mpd to your's bar! Settings listed above.
+Reequires
+ - python-mpd2 (NOT python2-mpd2)
+ # pip install python-mpd2
+
+@author shadowprince
+@license Eclipse Public License
+"""
+
+# mpd settings
+HOST = "localhost"
+PORT = "6600"
+PASSWORD = None
+
+# time to update
+CACHE_TIMEOUT = 1
+
+# position
+POSITION = 0
+
+# if text length will be greater - it'll shrink it
+MAX_WIDTH = 120
+
+# hide any indicator, if
+HIDE_WHEN_PAUSED = False
+HIDE_WHEN_STOPPED = True
+
+# state characters (or strings). Actual of them replaces {state} placeholder in STRFORMAT
+STATE_CHARACTERS = {
+ "pause": "[pause]",
+ "play": "[play]",
+ "stop": "[stop]",
+}
+
+""" format of result string
+can contain:
+ {state} - current state from STATE_CHARACTERS
+ Track information:
+ {track}, {artist}, {title}, {time}, {album}, {pos}
+ In additional, information about next track also comes in, in analogue with current, but with next_ prefix,
+ like {next_title}
+"""
+STRFORMAT = "{state} №{pos}. {artist} - {title} [{time}] | {next_title}"
+
+
+class Py3status:
+ def __init__(self):
+ self.text = ''
+
+ def currentTrack(self, i3status_output_json, i3status_config):
+ try:
+ c = MPDClient()
+ c.connect(host=HOST, port=PORT)
+ if PASSWORD:
+ c.password(PASSWORD)
+
+ status = c.status()
+ song = int(status.get("song", 0))
+ next_song = int(status.get("nextsong", 0))
+
+ if (status["state"] == "pause" and HIDE_WHEN_PAUSED) or (status["state"] == "stop" and HIDE_WHEN_STOPPED):
+ text = ""
+ else:
+ try:
+ song = c.playlistinfo()[song]
+ song["time"] = "{0:.2f}".format(int(song.get("time", 1)) / 60)
+ except IndexError:
+ song = {}
+
+ try:
+ next_song = c.playlistinfo()[next_song]
+ except IndexError:
+ next_song = {}
+
+ format_args = song
+ format_args["state"] = STATE_CHARACTERS.get(status.get("state", None))
+ for k, v in next_song.items():
+ format_args["next_{}".format(k)] = v
+
+ text = STRFORMAT
+ for k, v in format_args.items():
+ text = text.replace("{" + k + "}", v)
+
+ for sub in re.findall(r"{\S+?}", text):
+ text = text.replace(sub, "")
+ except SocketError:
+ text = "Failed to connect to mpd!"
+ except CommandError:
+ text = "Failed to authenticate to mpd!"
+ c.disconnect()
+
+ if len(text) > MAX_WIDTH:
+ text = text[-MAX_WIDTH-3:] + "..."
+
+ if self.text != text:
+ transformed = True
+ self.text = text
+ else:
+ transformed = False
+
+ response = {
+ 'cached_until': time() + CACHE_TIMEOUT,
+ 'full_text': self.text,
+ 'name': 'scratchpad-count',
+ 'transformed': transformed
+ }
+
+ return (POSITION, response)
diff --git a/modules/scratchpad-counter.py b/modules/scratchpad-counter.py
new file mode 100644
index 0000000..390745f
--- /dev/null
+++ b/modules/scratchpad-counter.py
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+
+import i3
+from time import time
+
+"""
+Module showing amount of windows at the scratchpad.
+
+@author shadowprince
+@license Eclipse Public License
+"""
+
+CACHE_TIMEOUT = 5
+HIDE_WHEN_NONE = False # hide indicator when there is no windows
+POSITION = 0
+STRFORMAT = "{} ⌫" # format of indicator. {} replaces with count of windows
+
+
+def find_scratch(tree):
+ if tree["name"] == "__i3_scratch":
+ return tree
+ else:
+ for x in tree["nodes"]:
+ result = find_scratch(x)
+ if result:
+ return result
+ return None
+
+
+class Py3status:
+ def __init__(self):
+ self.count = -1
+
+ def scratchpad_counter(self, i3status_output_json, i3status_config):
+ count = len(find_scratch(i3.get_tree()).get("floating_nodes", []))
+
+ if self.count != count:
+ transformed = True
+ self.count = count
+ else:
+ transformed = False
+
+ response = {
+ 'cached_until': time() + CACHE_TIMEOUT,
+ 'full_text': '' if HIDE_WHEN_NONE and count == 0 else STRFORMAT.format(count),
+ 'name': 'scratchpad-counter',
+ 'transformed': transformed
+ }
+
+ return (POSITION, response)
diff --git a/modules/window-title.py b/modules/window-title.py
new file mode 100644
index 0000000..4f6f062
--- /dev/null
+++ b/modules/window-title.py
@@ -0,0 +1,58 @@
+import i3
+from time import time
+
+"""
+Py3status plugin - shows current window title.
+
+Requires:
+ - i3-py (https://github.com/ziberna/i3-py)
+ # pip install i3-py
+
+If payload from server contains wierd utf-8
+(for example one window have something bad in title) - the plugin will
+give empty output UNTIL this window is closed.
+I can't fix or workaround that in PLUGIN, problem is in i3-py library.
+
+@author shadowprince
+@license Eclipse Public License
+"""
+
+CACHE_TIMEOUT = 0.5 # maximum time to update indicator
+MAX_WIDTH = 120 # if width of title is greater, shrink it and add '...'
+POSITION = 0
+
+
+def find_focused(tree):
+ if type(tree) == list:
+ for el in tree:
+ res = find_focused(el)
+ if res:
+ return res
+
+ elif type(tree) == dict:
+ if tree['focused']:
+ return tree
+ else:
+ return find_focused(tree['nodes'] + tree['floating_nodes'])
+
+
+class Py3status:
+ def __init__(self):
+ self.text = ''
+
+ def window_title(self, i3_status_output_json, i3status_config):
+ window = find_focused(i3.get_tree())
+
+ transformed = False
+ if window and 'name' in window and window['name'] != self.text:
+ self.text = len(window['name']) > MAX_WIDTH and "..." + window['name'][-(MAX_WIDTH-3):] or window['name']
+ transformed = True
+
+ response = {
+ 'cached_until': time() + CACHE_TIMEOUT,
+ 'full_text': self.text,
+ 'name': 'window-title',
+ 'transformed': transformed
+ }
+
+ return (POSITION, response)