3.24: Introduce time wrapper, remove battery remaining_* formatter(!!!)
This commit is contained in:
parent
3394aa56ea
commit
69b702d2f1
49
README.md
49
README.md
@ -19,6 +19,18 @@ status output compatible to i3status / i3bar of the i3 window manager.
|
||||
|
||||
* [Arch Linux](https://aur.archlinux.org/packages/i3pystatus-git/)
|
||||
|
||||
### Release Notes
|
||||
|
||||
#### 3.24
|
||||
|
||||
**This release introduced changes that may require manual changes to your
|
||||
configuration file**
|
||||
|
||||
* Introduced TimeWrapper
|
||||
* battery module: removed remaining_\* formatters in favor of TimeWrapper,
|
||||
as it can not only reproduce all the variants removed, but can do much more.
|
||||
* mpd: Uses TimeWrapper for song_length, song_elapsed
|
||||
|
||||
## Configuration
|
||||
|
||||
You can keep your config file at various places, i3pystatus will look
|
||||
@ -75,7 +87,7 @@ from network, wireless and pulseaudio in this example):
|
||||
# This would also display a desktop notification (via dbus) if the percentage
|
||||
# goes below 5 percent while discharging. The block will also color RED.
|
||||
status.register("battery",
|
||||
format="{status}/{consumption:.2f}W {percentage:.2f}% [{percentage_design:.2f}%] {remaining_hm}",
|
||||
format="{status}/{consumption:.2f}W {percentage:.2f}% [{percentage_design:.2f}%] {remaining:%E%hh:%Mm}",
|
||||
alert=True,
|
||||
alert_percentage=5,
|
||||
status={
|
||||
@ -185,6 +197,27 @@ Inside a group always all format specifiers must evaluate to true (logical and).
|
||||
You can nest groups. The inner group will only become part of the output if both
|
||||
the outer group and the inner group are eligible for output.
|
||||
|
||||
#### TimeWrapper
|
||||
|
||||
Some modules that output times use TimeWrapper to format these. TimeWrapper is
|
||||
a mere extension of the standard formatting method.
|
||||
|
||||
The time format that should be used is specified using the format specifier, i.e.
|
||||
with some_time being 3951 seconds a format string like `{some_time:%h:%m:%s}`
|
||||
would produce `1:5:51`
|
||||
|
||||
* `%h`, `%m` and `%s` are the hours, minutes and seconds without leading zeros
|
||||
(i.e. 0 to 59 for minutes and seconds)
|
||||
* `%H`, `%M` and `%S` are padded with a leading zero to two digits, i.e. 00 to 59
|
||||
* `%l` and `%L` produce hours non-padded and padded but only if hours is not zero.
|
||||
If the hours are zero it produces an empty string.
|
||||
* `%%` produces a literal %
|
||||
* `%E` (only valid on beginning of the string) if the time is null, don't format
|
||||
anything but rather produce an empty string. If the time is non-null it is
|
||||
removed from the string.
|
||||
* When the module in question also uses formatp, 0 seconds counts as "not known".
|
||||
* The formatted time is stripped, i.e. spaces on both ends of the result are removed
|
||||
|
||||
## Modules
|
||||
|
||||
|
||||
@ -244,8 +277,7 @@ battery status
|
||||
|
||||
Available formatters for format and alert_format_\*:
|
||||
|
||||
* `{remaining_str}` — remaining time for charging or discharging in the format H:MM
|
||||
* `{remaining_hm}`- remaining time in the format Hh:MMm
|
||||
* `{remaining}` — remaining time for charging or discharging, uses TimeWrapper formatting, default format is `%E%h:%M`
|
||||
* `{percentage}` — battery percentage relative to the last full value
|
||||
* `{percentage_design}` — absolute battery charge percentage
|
||||
* `{consumption (Watts)}` — current power flowing into/out of the battery
|
||||
@ -256,13 +288,13 @@ Available formatters for format and alert_format_\*:
|
||||
__Settings:__
|
||||
|
||||
* `battery_ident` — The name of your battery, usually BAT0 or BAT1 (default: `BAT0`)
|
||||
* `format` — (default: `{status} {remaining_hm}`)
|
||||
* `format` — (default: `{status} {remaining}`)
|
||||
* `alert` — Display a libnotify-notification on low battery (default: `False`)
|
||||
* `alert_percentage` — (default: `10`)
|
||||
* `alert_format_title` — The title of the notification, all formatters can be used (default: `Low battery`)
|
||||
* `alert_format_body` — The body text of the notification, all formatters can be used (default: `Battery {battery_ident} has only {percentage:.2f}% ({remaining_hm}) remaining!`)
|
||||
* `path` — Override the default-generated path (default: `None`)
|
||||
* `status` — A dictionary mapping ('DIS', 'CHR', 'FULL') to alternative names (default: `{'FULL': 'FULL', 'DIS': 'DIS', 'CHR': 'CHR'}`)
|
||||
* `status` — A dictionary mapping ('DIS', 'CHR', 'FULL') to alternative names (default: `{'CHR': 'CHR', 'DIS': 'DIS', 'FULL': 'FULL'}`)
|
||||
|
||||
|
||||
|
||||
@ -431,8 +463,8 @@ Available formatters (uses formatp)
|
||||
* `{title}` — (the title of the current song)
|
||||
* `{album}` — (the album of the current song, can be an empty string (e.g. for online streams))
|
||||
* `{artist}` — (can be empty, too)
|
||||
* `{song_elapsed}` — (Position in the currently playing song, looks like 3:54)
|
||||
* `{song_length}` — (Length of the current song, same format as song_elapsed)
|
||||
* `{song_elapsed}` — (Position in the currently playing song, **uses TimeWrapper**, default is `%m:%S`)
|
||||
* `{song_length}` — (Length of the current song, same as song_elapsed)
|
||||
* `{pos}` — (Position of current song in playlist, one-based)
|
||||
* `{len}` — (Songs in playlist)
|
||||
* `{status}` — (play, pause, stop mapped through the `status` dictionary)
|
||||
@ -447,7 +479,7 @@ __Settings:__
|
||||
* `host` — (default: `localhost`)
|
||||
* `port` — MPD port (default: `6600`)
|
||||
* `format` — formatp string (default: `{title} {status}`)
|
||||
* `status` — Dictionary mapping pause, play and stop to output (default: `{'stop': '◾', 'pause': '▷', 'play': '▶'}`)
|
||||
* `status` — Dictionary mapping pause, play and stop to output (default: `{'play': '▶', 'stop': '◾', 'pause': '▷'}`)
|
||||
|
||||
|
||||
|
||||
@ -503,6 +535,7 @@ Shows volume of default PulseAudio sink (output).
|
||||
Available formatters:
|
||||
* `{volume}` — volume in percent (0...100)
|
||||
* `{db}` — volume in decibels relative to 100 %, i.e. 100 % = 0 dB, 50 % = -18 dB, 0 % = -infinity dB
|
||||
(the literal value for -infinity is `-∞`)
|
||||
|
||||
|
||||
__Settings:__
|
||||
|
@ -19,6 +19,18 @@ status output compatible to i3status / i3bar of the i3 window manager.
|
||||
|
||||
* [Arch Linux](https://aur.archlinux.org/packages/i3pystatus-git/)
|
||||
|
||||
### Release Notes
|
||||
|
||||
#### 3.24
|
||||
|
||||
**This release introduced changes that may require manual changes to your
|
||||
configuration file**
|
||||
|
||||
* Introduced TimeWrapper
|
||||
* battery module: removed remaining_\* formatters in favor of TimeWrapper,
|
||||
as it can not only reproduce all the variants removed, but can do much more.
|
||||
* mpd: Uses TimeWrapper for song_length, song_elapsed
|
||||
|
||||
## Configuration
|
||||
|
||||
You can keep your config file at various places, i3pystatus will look
|
||||
@ -75,7 +87,7 @@ from network, wireless and pulseaudio in this example):
|
||||
# This would also display a desktop notification (via dbus) if the percentage
|
||||
# goes below 5 percent while discharging. The block will also color RED.
|
||||
status.register("battery",
|
||||
format="{status}/{consumption:.2f}W {percentage:.2f}% [{percentage_design:.2f}%] {remaining_hm}",
|
||||
format="{status}/{consumption:.2f}W {percentage:.2f}% [{percentage_design:.2f}%] {remaining:%E%hh:%Mm}",
|
||||
alert=True,
|
||||
alert_percentage=5,
|
||||
status={
|
||||
@ -185,6 +197,27 @@ Inside a group always all format specifiers must evaluate to true (logical and).
|
||||
You can nest groups. The inner group will only become part of the output if both
|
||||
the outer group and the inner group are eligible for output.
|
||||
|
||||
#### TimeWrapper
|
||||
|
||||
Some modules that output times use TimeWrapper to format these. TimeWrapper is
|
||||
a mere extension of the standard formatting method.
|
||||
|
||||
The time format that should be used is specified using the format specifier, i.e.
|
||||
with some_time being 3951 seconds a format string like `{some_time:%h:%m:%s}`
|
||||
would produce `1:5:51`
|
||||
|
||||
* `%h`, `%m` and `%s` are the hours, minutes and seconds without leading zeros
|
||||
(i.e. 0 to 59 for minutes and seconds)
|
||||
* `%H`, `%M` and `%S` are padded with a leading zero to two digits, i.e. 00 to 59
|
||||
* `%l` and `%L` produce hours non-padded and padded but only if hours is not zero.
|
||||
If the hours are zero it produces an empty string.
|
||||
* `%%` produces a literal %
|
||||
* `%E` (only valid on beginning of the string) if the time is null, don't format
|
||||
anything but rather produce an empty string. If the time is non-null it is
|
||||
removed from the string.
|
||||
* When the module in question also uses formatp, 0 seconds counts as "not known".
|
||||
* The formatted time is stripped, i.e. spaces on both ends of the result are removed
|
||||
|
||||
## Modules
|
||||
|
||||
!!module_doc!!
|
||||
|
@ -4,9 +4,9 @@
|
||||
import re
|
||||
import configparser
|
||||
|
||||
from . import IntervalModule
|
||||
from .core.util import PrefixedKeyDict, lchop
|
||||
from .core.desktop import display_notification
|
||||
from i3pystatus import IntervalModule
|
||||
from i3pystatus.core.util import PrefixedKeyDict, lchop, TimeWrapper
|
||||
from i3pystatus.core.desktop import display_notification
|
||||
|
||||
class UEventParser(configparser.ConfigParser):
|
||||
@staticmethod
|
||||
@ -78,18 +78,6 @@ class BatteryEnergy(Battery):
|
||||
else:
|
||||
return (self.bat["ENERGY_FULL"] - self.bat["ENERGY_NOW"]) / self.bat["POWER_NOW"] * 60
|
||||
|
||||
def format_remaining(minutes, prefix):
|
||||
hours, minutes = map(int, divmod(minutes, 60))
|
||||
|
||||
d = PrefixedKeyDict(prefix)
|
||||
d.update({
|
||||
"str": "{}:{:02}".format(hours, minutes),
|
||||
"hm": "{}h:{:02}m".format(hours, minutes),
|
||||
"hours": hours,
|
||||
"mins": minutes,
|
||||
})
|
||||
return d
|
||||
|
||||
class BatteryChecker(IntervalModule):
|
||||
"""
|
||||
This class uses the /sys/class/power_supply/…/uevent interface to check for the
|
||||
@ -97,8 +85,7 @@ class BatteryChecker(IntervalModule):
|
||||
|
||||
Available formatters for format and alert_format_\*:
|
||||
|
||||
* `{remaining_str}` — remaining time for charging or discharging in the format H:MM
|
||||
* `{remaining_hm}`- remaining time in the format Hh:MMm
|
||||
* `{remaining}` — remaining time for charging or discharging, uses TimeWrapper formatting, default format is `%E%h:%M`
|
||||
* `{percentage}` — battery percentage relative to the last full value
|
||||
* `{percentage_design}` — absolute battery charge percentage
|
||||
* `{consumption (Watts)}` — current power flowing into/out of the battery
|
||||
@ -117,7 +104,7 @@ class BatteryChecker(IntervalModule):
|
||||
("status", "A dictionary mapping ('DIS', 'CHR', 'FULL') to alternative names"),
|
||||
)
|
||||
battery_ident = "BAT0"
|
||||
format = "{status} {remaining_hm}"
|
||||
format = "{status} {remaining}"
|
||||
status = {
|
||||
"CHR": "CHR",
|
||||
"DIS": "DIS",
|
||||
@ -143,17 +130,16 @@ class BatteryChecker(IntervalModule):
|
||||
|
||||
fdict = {
|
||||
"battery_ident": self.battery_ident,
|
||||
"remaining_str": "",
|
||||
"remaining_hm": "",
|
||||
"percentage": battery.percentage(),
|
||||
"percentage_design": battery.percentage(design=True),
|
||||
"consumption": battery.consumption(),
|
||||
"remaining": TimeWrapper(0, "%E%h:%M"),
|
||||
}
|
||||
|
||||
status = battery.status()
|
||||
if status in ["Discharging", "Charging"]:
|
||||
remaining = battery.remaining()
|
||||
fdict.update(format_remaining(remaining, "remaining_"))
|
||||
fdict["remaining"] = TimeWrapper(remaining * 60, "%E%h:%M")
|
||||
if status == "Discharging":
|
||||
fdict["status"] = "DIS"
|
||||
if remaining < 15:
|
||||
|
@ -2,6 +2,7 @@
|
||||
import collections
|
||||
import itertools
|
||||
import re
|
||||
import string
|
||||
|
||||
from i3pystatus.core.exceptions import *
|
||||
from i3pystatus.core.imputil import ClassFinder
|
||||
@ -240,3 +241,32 @@ def formatp(string, **kwargs):
|
||||
return merge_tree(tree)
|
||||
|
||||
formatp.field_re = re.compile(r"({(\w+)[^}]*})")
|
||||
|
||||
class TimeWrapper:
|
||||
class TimeTemplate(string.Template):
|
||||
delimiter = "%"
|
||||
idpattern = r"[a-zA-Z]"
|
||||
|
||||
def __init__(self, seconds, default_format="%m:%S"):
|
||||
self.seconds = int(seconds)
|
||||
self.default_format = default_format
|
||||
|
||||
def __bool__(self):
|
||||
return bool(self.seconds)
|
||||
|
||||
def __format__(self, format_spec):
|
||||
format_spec = format_spec or self.default_format
|
||||
h = self.seconds // 3600
|
||||
m, s = divmod(self.seconds % 3600, 60)
|
||||
l = h if h else ""
|
||||
L = "%02d" % h if h else ""
|
||||
|
||||
if format_spec.startswith("%E"):
|
||||
format_spec = format_spec[2:]
|
||||
if not self.seconds:
|
||||
return ""
|
||||
return self.TimeTemplate(format_spec).substitute(
|
||||
h=h, m=m, s=s,
|
||||
H="%02d" % h, M="%02d" % m, S="%02d" % s,
|
||||
l=l, L=L,
|
||||
).strip()
|
||||
|
@ -2,6 +2,7 @@
|
||||
import socket
|
||||
|
||||
from i3pystatus import IntervalModule, formatp
|
||||
from i3pystatus.core.util import TimeWrapper
|
||||
|
||||
def format_time(seconds):
|
||||
return "{}:{:02}".format(*divmod(int(seconds), 60)) if seconds else ""
|
||||
@ -14,8 +15,8 @@ class MPD(IntervalModule):
|
||||
* `{title}` — (the title of the current song)
|
||||
* `{album}` — (the album of the current song, can be an empty string (e.g. for online streams))
|
||||
* `{artist}` — (can be empty, too)
|
||||
* `{song_elapsed}` — (Position in the currently playing song, looks like 3:54)
|
||||
* `{song_length}` — (Length of the current song, same format as song_elapsed)
|
||||
* `{song_elapsed}` — (Position in the currently playing song, **uses TimeWrapper**, default is `%m:%S`)
|
||||
* `{song_length}` — (Length of the current song, same as song_elapsed)
|
||||
* `{pos}` — (Position of current song in playlist, one-based)
|
||||
* `{len}` — (Songs in playlist)
|
||||
* `{status}` — (play, pause, stop mapped through the `status` dictionary)
|
||||
@ -78,8 +79,8 @@ class MPD(IntervalModule):
|
||||
"title": currentsong.get("Title", ""),
|
||||
"album": currentsong.get("Album", ""),
|
||||
"artist": currentsong.get("Artist", ""),
|
||||
"song_length": format_time(currentsong.get("Time", 0)),
|
||||
"song_elapsed": format_time(float(status.get("elapsed", 0))),
|
||||
"song_length": TimeWrapper(currentsong.get("Time", 0)),
|
||||
"song_elapsed": TimeWrapper(float(status.get("elapsed", 0))),
|
||||
"bitrate": int(status.get("bitrate", 0)),
|
||||
|
||||
}
|
||||
|
2
setup.py
2
setup.py
@ -3,7 +3,7 @@
|
||||
from setuptools import setup
|
||||
|
||||
setup(name="i3pystatus",
|
||||
version="3.23",
|
||||
version="3.24",
|
||||
description="Like i3status, this generates status line for i3bar / i3wm",
|
||||
url="http://github.com/enkore/i3pystatus",
|
||||
license="MIT",
|
||||
|
@ -266,6 +266,8 @@ class FormatPTests(unittest.TestCase):
|
||||
def test_side_by_side(self):
|
||||
s = "{status} [{artist} / [{album} / ]]{title}[ {song_elapsed}/{song_length}]"
|
||||
assert util.formatp(s, status="▷", title="Only For The Weak", song_elapsed="1:41", song_length="4:55") == "▷ Only For The Weak 1:41/4:55"
|
||||
assert util.formatp(s, status="", album="Foo", title="Die, Die, Crucified", song_elapsed="2:52") == " Die, Die, Crucified"
|
||||
assert util.formatp("[[{a}][{b}]]", b=1) == "1"
|
||||
|
||||
def test_complex_field(self):
|
||||
class NS:
|
||||
|
Loading…
Reference in New Issue
Block a user