From 386fd4322069fec73772b8db151ad75f7c1aa34b Mon Sep 17 00:00:00 2001 From: Robert Günzler Date: Sun, 28 Jun 2020 23:42:38 +0200 Subject: waybar: add redshift module --- .config/redshift/hooks/notif.sh | 10 +- .config/waybar/config | 9 +- .config/waybar/mediaplayer.py | 169 ---------------------------------- .config/waybar/modules/mediaplayer.py | 169 ++++++++++++++++++++++++++++++++++ .config/waybar/modules/redshift.go | 92 ++++++++++++++++++ .config/waybar/modules/toggl.lua | 28 ++++++ .config/waybar/style.css | 24 +++++ .config/waybar/toggl.lua | 28 ------ 8 files changed, 325 insertions(+), 204 deletions(-) delete mode 100755 .config/waybar/mediaplayer.py create mode 100755 .config/waybar/modules/mediaplayer.py create mode 100644 .config/waybar/modules/redshift.go create mode 100755 .config/waybar/modules/toggl.lua delete mode 100755 .config/waybar/toggl.lua diff --git a/.config/redshift/hooks/notif.sh b/.config/redshift/hooks/notif.sh index 667f9e1..de04d6c 100755 --- a/.config/redshift/hooks/notif.sh +++ b/.config/redshift/hooks/notif.sh @@ -2,11 +2,9 @@ case "$1" in period-changed) - notify-send "redshift" "changed to $3" - if [ "$3" = "night" ]; then - ogurictl output --image ~/.background_night '*' - else - ogurictl output --image ~/.background '*' - fi + case "$3" in + transition|night) pkill -USR2 waybar-redshift ;; + none|day) pkill -USR1 waybar-redshift ;; + esac ;; esac diff --git a/.config/waybar/config b/.config/waybar/config index a8cb36a..9e3dfec 100644 --- a/.config/waybar/config +++ b/.config/waybar/config @@ -6,7 +6,7 @@ "modules-left": ["sway/mode", "sway/workspaces", "sway/window"], "modules-center": ["custom/media", "custom/toggl", "clock#local", "clock#utc"], - "modules-right": ["tray", "cpu", "memory", "disk#root", "disk#home", "temperature", "idle_inhibitor", "backlight", "pulseaudio", "network#wg", "network", "battery"], + "modules-right": ["tray", "cpu", "memory", "disk#root", "disk#home", "temperature", "idle_inhibitor", "backlight", "custom/redshift", "pulseaudio", "network#wg", "network", "battery"], "sway/workspaces": { "disable-scroll": true, "all-outputs": false, @@ -130,6 +130,13 @@ "exec": "$HOME/.config/waybar/toggl.lua 2>/dev/null", "interval": 30 }, + "custom/redshift": { + "exec-if": "pgrep redshift", + "exec": "$HOME/devel/projects/waybar-bites/redshift/waybar-redshift", + "return-type": "json", + "on-click": "pkill -x -USR1 redshift", + "on-click-right": "pkill -x -HUP waybar-redshift" + }, "mpd": { "format": "{stateIcon} {artist} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S})", "format-disconnected": "", diff --git a/.config/waybar/mediaplayer.py b/.config/waybar/mediaplayer.py deleted file mode 100755 index 4d80ffa..0000000 --- a/.config/waybar/mediaplayer.py +++ /dev/null @@ -1,169 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import json -import logging -import signal -import sys -from datetime import timedelta -from math import floor - -# yapf: disable -import gi -gi.require_version('Playerctl', '2.0') -from gi.repository import GLib, Playerctl -# yapf: enable - -logger = logging.getLogger(__name__) - - -def write_output(text, player): - logger.info('Writing output') - - output = { - 'text': text, - 'class': 'custom-' + player.props.player_name, - 'alt': player.props.player_name - } - - sys.stdout.write(json.dumps(output) + '\n') - sys.stdout.flush() - - -def on_play(player, status, manager): - logger.info('Received new playback status') - on_metadata(player, player.props.metadata, manager) - - -def on_metadata(player, metadata, manager): - logger.info('Received new metadata') - track_info = '' - - if player.props.player_name == 'spotify' and \ - 'mpris:trackid' in metadata.keys() and \ - ':ad:' in player.props.metadata['mpris:trackid']: - track_info = 'AD PLAYING' - - if player.get_title() and player.get_title() != '': - track_info = f"{player.get_title()}" - if player.get_artist() and player.get_artist() != '': - track_info = f"{player.get_artist()} - {track_info}" - - if 'mpris:length' in player.props.metadata.keys(): - dur_td = timedelta(microseconds=player.props.metadata['mpris:length']) - dur_h, remainder = divmod(dur_td.total_seconds(), 3600) - dur_m, dur_s = divmod(remainder, 60) - dur = f"{floor(dur_m)}:{floor(dur_s):02}" - if dur_h > 0: - dur = f"{floor(dur_h)}:{dur}" - track_info = '{} ({})'.format(track_info, str(dur)) - - if player.props.status == 'Paused' and track_info: - track_info = ' ' + track_info - - if player.props.status == 'Stopped': - return - - if track_info == '': - logger.warning('TrackInfo was empty after parsing') - return - - write_output(track_info, player) - - -def on_player_appeared(manager, player, selected_player=None, ignored_players=None): - if player is not None \ - and (selected_player is None or player.name == selected_player) \ - and not (player.name in ignored_players): - init_player(manager, player) - else: - logger.debug( - "New player appeared, but it's not the selected player, skipping") - - -def on_player_vanished(manager, player): - logger.info('Player has vanished') - sys.stdout.write('\n') - sys.stdout.flush() - - -def init_player(manager, name): - logger.debug('Initialize player: {player}'.format(player=name.name)) - player = Playerctl.Player.new_from_name(name) - player.connect('playback-status', on_play, manager) - player.connect('metadata', on_metadata, manager) - manager.manage_player(player) - on_metadata(player, player.props.metadata, manager) - - -def signal_handler(sig, frame): - logger.debug('Received signal to stop, exiting') - sys.stdout.write('\n') - sys.stdout.flush() - # loop.quit() - sys.exit(0) - - -def parse_arguments(): - parser = argparse.ArgumentParser() - - # Increase verbosity with every occurance of -v - parser.add_argument('-v', '--verbose', action='count', default=0) - - # Define for which player we're listening - parser.add_argument('--player', - help='player to listen for') - - # Define which players to ignore entirely - parser.add_argument('-I', '--ignore', - action='append', - help='players to ignore') - - return parser.parse_args() - - -def main(): - arguments = parse_arguments() - - # Initialize logging - logging.basicConfig(stream=sys.stderr, - level=logging.DEBUG, - format='%(name)s %(levelname)s %(message)s') - - # Logging is set by default to WARN and higher. - # With every occurrence of -v it's lowered by one - logger.setLevel(max((3 - arguments.verbose) * 10, 0)) - - # Log the sent command line arguments - logger.debug('Arguments received {}'.format(vars(arguments))) - - manager = Playerctl.PlayerManager() - loop = GLib.MainLoop() - - manager.connect('name-appeared', - lambda *args: on_player_appeared(*args, arguments.player, arguments.ignore)) - manager.connect('player-vanished', on_player_vanished) - - signal.signal(signal.SIGINT, signal_handler) - signal.signal(signal.SIGTERM, signal_handler) - - for player in manager.props.player_names: - if arguments.player is not None and arguments.player != player.name: - logger.debug( - '{player} is not the filtered player, skipping it'.format( - player=player.name)) - continue - - if arguments.ignore is not None and player.name in arguments.ignore: - logger.debug( - '{player} is ignored, skipping it'.format( - player=player.name)) - continue - - init_player(manager, player) - - loop.run() - - -if __name__ == '__main__': - main() diff --git a/.config/waybar/modules/mediaplayer.py b/.config/waybar/modules/mediaplayer.py new file mode 100755 index 0000000..4d80ffa --- /dev/null +++ b/.config/waybar/modules/mediaplayer.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python3 + +import argparse +import json +import logging +import signal +import sys +from datetime import timedelta +from math import floor + +# yapf: disable +import gi +gi.require_version('Playerctl', '2.0') +from gi.repository import GLib, Playerctl +# yapf: enable + +logger = logging.getLogger(__name__) + + +def write_output(text, player): + logger.info('Writing output') + + output = { + 'text': text, + 'class': 'custom-' + player.props.player_name, + 'alt': player.props.player_name + } + + sys.stdout.write(json.dumps(output) + '\n') + sys.stdout.flush() + + +def on_play(player, status, manager): + logger.info('Received new playback status') + on_metadata(player, player.props.metadata, manager) + + +def on_metadata(player, metadata, manager): + logger.info('Received new metadata') + track_info = '' + + if player.props.player_name == 'spotify' and \ + 'mpris:trackid' in metadata.keys() and \ + ':ad:' in player.props.metadata['mpris:trackid']: + track_info = 'AD PLAYING' + + if player.get_title() and player.get_title() != '': + track_info = f"{player.get_title()}" + if player.get_artist() and player.get_artist() != '': + track_info = f"{player.get_artist()} - {track_info}" + + if 'mpris:length' in player.props.metadata.keys(): + dur_td = timedelta(microseconds=player.props.metadata['mpris:length']) + dur_h, remainder = divmod(dur_td.total_seconds(), 3600) + dur_m, dur_s = divmod(remainder, 60) + dur = f"{floor(dur_m)}:{floor(dur_s):02}" + if dur_h > 0: + dur = f"{floor(dur_h)}:{dur}" + track_info = '{} ({})'.format(track_info, str(dur)) + + if player.props.status == 'Paused' and track_info: + track_info = ' ' + track_info + + if player.props.status == 'Stopped': + return + + if track_info == '': + logger.warning('TrackInfo was empty after parsing') + return + + write_output(track_info, player) + + +def on_player_appeared(manager, player, selected_player=None, ignored_players=None): + if player is not None \ + and (selected_player is None or player.name == selected_player) \ + and not (player.name in ignored_players): + init_player(manager, player) + else: + logger.debug( + "New player appeared, but it's not the selected player, skipping") + + +def on_player_vanished(manager, player): + logger.info('Player has vanished') + sys.stdout.write('\n') + sys.stdout.flush() + + +def init_player(manager, name): + logger.debug('Initialize player: {player}'.format(player=name.name)) + player = Playerctl.Player.new_from_name(name) + player.connect('playback-status', on_play, manager) + player.connect('metadata', on_metadata, manager) + manager.manage_player(player) + on_metadata(player, player.props.metadata, manager) + + +def signal_handler(sig, frame): + logger.debug('Received signal to stop, exiting') + sys.stdout.write('\n') + sys.stdout.flush() + # loop.quit() + sys.exit(0) + + +def parse_arguments(): + parser = argparse.ArgumentParser() + + # Increase verbosity with every occurance of -v + parser.add_argument('-v', '--verbose', action='count', default=0) + + # Define for which player we're listening + parser.add_argument('--player', + help='player to listen for') + + # Define which players to ignore entirely + parser.add_argument('-I', '--ignore', + action='append', + help='players to ignore') + + return parser.parse_args() + + +def main(): + arguments = parse_arguments() + + # Initialize logging + logging.basicConfig(stream=sys.stderr, + level=logging.DEBUG, + format='%(name)s %(levelname)s %(message)s') + + # Logging is set by default to WARN and higher. + # With every occurrence of -v it's lowered by one + logger.setLevel(max((3 - arguments.verbose) * 10, 0)) + + # Log the sent command line arguments + logger.debug('Arguments received {}'.format(vars(arguments))) + + manager = Playerctl.PlayerManager() + loop = GLib.MainLoop() + + manager.connect('name-appeared', + lambda *args: on_player_appeared(*args, arguments.player, arguments.ignore)) + manager.connect('player-vanished', on_player_vanished) + + signal.signal(signal.SIGINT, signal_handler) + signal.signal(signal.SIGTERM, signal_handler) + + for player in manager.props.player_names: + if arguments.player is not None and arguments.player != player.name: + logger.debug( + '{player} is not the filtered player, skipping it'.format( + player=player.name)) + continue + + if arguments.ignore is not None and player.name in arguments.ignore: + logger.debug( + '{player} is ignored, skipping it'.format( + player=player.name)) + continue + + init_player(manager, player) + + loop.run() + + +if __name__ == '__main__': + main() diff --git a/.config/waybar/modules/redshift.go b/.config/waybar/modules/redshift.go new file mode 100644 index 0000000..43b623c --- /dev/null +++ b/.config/waybar/modules/redshift.go @@ -0,0 +1,92 @@ +// usage: +// go build -o waybar-redshift redshift.go +package main + +import ( + "encoding/json" + "fmt" + "os" + "os/exec" + "os/signal" + "strings" + + . "syscall" +) + +const ( + ICON = "" +) + +func main() { + var ( + update = make(chan os.Signal, 1) + cancel = make(chan os.Signal, 1) + ) + signal.Notify(update, SIGUSR1, SIGUSR2, SIGHUP) + signal.Notify(cancel, SIGTERM, SIGINT) + + // could use golang.org/x/sys/unix.Prctl here + // but I want to avoid the dependency + if _, _, e := Syscall6(SYS_PRCTL, + uintptr(PR_SET_PDEATHSIG), + uintptr(SIGTERM), + 0, 0, 0, 0); e != 0 { + os.Exit(1) + } + // if err := Prctl(PR_SET_PDEATHSIG, + // SIGTERM, + // 0, 0, 0); err != nil { + // fmt.Fprintln(os.Stderr, err) + // os.Exit(1) + // } + + lookup() + for { + select { + case sig := <-update: + switch sig { + case SIGUSR1: + // redshift off + printJson("day", "day", ICON) + case SIGUSR2: + // redshift on + printJson("night", "night", ICON) + case SIGHUP: + lookup() + } + case <-cancel: + fmt.Fprintln(os.Stderr, "bye") + os.Exit(0) + } + } +} + +func printJson(class, tooltip, text string) { + _ = json.NewEncoder(os.Stdout).Encode(map[string]string{ + "class": class, + "tooltip": "redshift: " + tooltip, + "text": text, + }) +} + +func lookup() { + b, err := exec.Command("redshift", "-p").Output() + if err != nil { + return + } + fds := strings.Fields(string(b)) + for idx, f := range fds { + if f == "Period:" { + fds = fds[idx+1:] + break + } + } + switch fds[0] { + case "Night": + printJson("night", "night", ICON) + case "Transition": + printJson("night", "transition", ICON) + case "Day": + printJson("day", "day", ICON) + } +} diff --git a/.config/waybar/modules/toggl.lua b/.config/waybar/modules/toggl.lua new file mode 100755 index 0000000..e44fce2 --- /dev/null +++ b/.config/waybar/modules/toggl.lua @@ -0,0 +1,28 @@ +#!/usr/bin/env luajit + +TOKEN = os.getenv("TOGGL_TOKEN") +if not TOKEN then + io.stderr:write("need TOGGL_TOKEN\n") + os.exit(1) +end + +function timer_duration() + cmd = string.format('toggl -t %s -j timer ls | jq -r ".[0].duration"', TOKEN) + local f = assert(io.popen(cmd, 'r')) + local s = assert(f:read('*a')) + f:close() + if not (s == '') then + return tonumber(s) + end +end + +unix_time = os.time() + +diff = timer_duration() +if not diff then + os.exit(0) +end + +d = unix_time + diff + +print(string.format(' %dh %.2dm', d/(60*60), d/60%60)) diff --git a/.config/waybar/style.css b/.config/waybar/style.css index 0b7016c..6fbbb03 100644 --- a/.config/waybar/style.css +++ b/.config/waybar/style.css @@ -26,6 +26,7 @@ #pulseaudio, #custom-media, #custom-toggl, +#custom-redshift, #mpd, #tray, #idle_inhibitor { @@ -178,6 +179,29 @@ label:focus { border-top: 2px solid #DB4D60; } +@keyframes flash { + 50% { + background: rgba(255,174,66, 0.7); + color: @background; + } +} +#custom-redshift.night { + border-top: 2px solid #ffae42; + color: @foreground; + animation-name: flash; + animation-duration: 0.3s; + animation-timing-function: linear; + animation-iteration-count: 1; +} +#custom-redshift.day { + color: @dimmed; +} +#custom-redshift.off, +#custom-redshift.unknown { + border-top: 2px solid @urgent; + color: @dimmed; +} + #mpd { color: @foreground; border-top: 2px solid #d75f00; diff --git a/.config/waybar/toggl.lua b/.config/waybar/toggl.lua deleted file mode 100755 index e44fce2..0000000 --- a/.config/waybar/toggl.lua +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env luajit - -TOKEN = os.getenv("TOGGL_TOKEN") -if not TOKEN then - io.stderr:write("need TOGGL_TOKEN\n") - os.exit(1) -end - -function timer_duration() - cmd = string.format('toggl -t %s -j timer ls | jq -r ".[0].duration"', TOKEN) - local f = assert(io.popen(cmd, 'r')) - local s = assert(f:read('*a')) - f:close() - if not (s == '') then - return tonumber(s) - end -end - -unix_time = os.time() - -diff = timer_duration() -if not diff then - os.exit(0) -end - -d = unix_time + diff - -print(string.format(' %dh %.2dm', d/(60*60), d/60%60)) -- cgit 1.4.1