(defvar draw false)
; window
(defwindow niri
:monitor 0
:namespace "niri"
:stacking "fg"
:exclusive false
:geometry (geometry
:height "100%"
:width "26px"
:anchor "left center"
:x "0px"
:y "0px"
)
(eventbox
:onscroll "eww update draw=true"
:onhoverlost "eww update draw=false"
(box
:orientation "horizontal"
:space-evenly false
(revealer
:reveal draw
:transition "slideright"
:duration "150ms"
(draw)
)
(bar)
)
)
)
; bar
(defwidget bar []
(box
:class "bar"
:orientation "horizontal"
(centerbox
:orientation "vertical"
(box
:class "top"
:orientation "vertical"
:valign "start"
:spacing 3
:space-evenly false
(box)
(block :class "char" (char))
(box)
(block :class "tray" (tray))
)
(box
:class "mid"
:orientation "vertical"
:valign "center"
:spacing 3
:space-evenly false
(block :class "spaces" (spaces))
)
(box
:class "bot"
:orientation "vertical"
:valign "end"
:spacing 3
:space-evenly false
(box)
(box :class "reco" (reco))
(box)
(box :class "idle" (idle))
(box)
(box :class "volu" (volu))
(box)
(box :class "date" (date))
)
)
)
)
; draw
(defwidget draw []
(box
:class "draw"
:space-evenly false
(box
:orientation "horizontal"
:space-evenly false
(centerbox
:orientation "vertical"
(box
:class "top"
:orientation "vertical"
:valign "start"
:spacing 3
:space-evenly false
; (block :class "charge" (charge) (charge-indicator))
(block :class "network" (network) (network-indicator))
(block :class "bluetooth" (bluetooth) (bluetooth-indicator))
; (block :class "sound" (sound) (sound-indicator))
(label
:class "player"
:style "padding: 0px 8px;"
:xalign 0
:show-truncated true
:wrap true
:limit-width 20
:text "${player.artist}\\n${player.album}\\n${player.title}\\n${player.status}"
)
(label
:class "notifications"
:style "padding: 0px 8px;"
:xalign 0
:show-truncated true
:wrap true
:limit-width 20
:text notifications
)
)
(box
:class "mid"
:orientation "vertical"
:valign "center"
:spacing 3
:space-evenly false
(block
:class "mem"
(mem-indicator)
; (mem)
)
(block
:class "cpu"
(cpu-indicator)
; (cpu)
)
(block
:class "disk"
(disk-indicator)
; (disk)
)
)
(box
:class "bot"
:orientation "vertical"
:valign "end"
:spacing 3
:space-evenly false
(block :class "moon" (moon) (box))
(label
:class "cal"
:style "padding: 0px 8px;"
:xalign 0
:show-truncated false
:unindent true
:markup cal
)
; (calendar
; :class "calendar"
; :show-heading false
; :show-day-names false
; )
; (label
; :unindent true
; :class "clipboard"
; :xalign 0
; :show-truncated false
; :text clipboard
; )
)
)
)
)
)
; block
(defwidget block [?class]
(box
:class "block ${class ?: ''}"
(box
:orientation "horizontal"
:space-evenly false
(box
:width 26
(children :nth 0)
)
(box
:hexpand true
:halign "end"
:orientation "vertical"
(children :nth 1)
)
)
)
)
(defwidget date []
(box
:orientation "vertical"
(label :text {date.month})
(label :text {date.day})
(label :style "color: #fff;" :text {date.hour})
(label :style "color: #fff;" :text {date.minute})
)
)
(defwidget reco []
(eventbox
:onclick `pgrep -x wf-recorder > /dev/null && pkill -SIGINT wf-recorder || sh -c 'selection=$(slurp) && [ -n "$selection" ] && wf-recorder --audio=@DEFAULT_SINK@.monitor -g "$selection" -f "$HOME/shot/reco $(date +\"%Y-%m-%d %H-%M-%S\").mp4" > /dev/null 2>&1 &'`
:cursor "pointer"
(circular-progress
:style "color: ${reco == 'true' ? '#cc6666' : '#444'};"
:height 6
:thickness 3
:value 100
)
)
)
(defwidget sound-indicator []
(asciibar
:text {sound.muted ? "──" : sound.bluetooth ? "BT" : ""}
:value {sound.volume}
)
)
(defwidget sound []
(eventbox
:onclick "amixer set Master toggle"
:cursor "pointer"
(label
:xalign 0
:limit-width 20
:show-truncated false
:text "${sound.device}"
)
)
)
(defwidget charge-indicator []
(asciibar
:text "${EWW_BATTERY == '' ? '' : '░░'}"
:value {EWW_BATTERY == '' ? 100 : EWW_BATTERY?.BAT0?.capacity ?: EWW_BATTERY?.BAT1?.capacity ?: EWW_BATTERY?.BAT?.capacity ?: 100}
:style {styles.charge}
)
)
(defwidget charge []
(asciilabel
:text "${EWW_BATTERY == '' ? '' : EWW_BATTERY?.BAT0?.status ?: EWW_BATTERY?.BAT1?.status ?: EWW_BATTERY?.BAT?.status ?: '?'}"
:value {EWW_BATTERY == '' ? 100 : EWW_BATTERY?.BAT0?.capacity ?: EWW_BATTERY?.BAT1?.capacity ?: EWW_BATTERY?.BAT?.capacity ?: 100}
:style {styles.charge}
)
)
(defwidget cpu-indicator []
(box
:orientation "vertical"
(for w in {EWW_CPU.cores}
(asciibar
:value {w.usage}
:style {styles.cpu}
)
)
)
)
(defwidget cpu []
(box
:orientation "vertical"
(for w in {EWW_CPU.cores}
(label :show-truncated false :xalign 0 :text "${w.freq}")
)
)
)
(defwidget mem-indicator []
(asciibar
:value {EWW_RAM.used_mem_perc}
:style {styles.mem}
)
)
(defwidget mem []
(asciilabel
:text "${round(EWW_RAM.used_mem / 1024 / 1024 / 1024, 3)} G"
:value {EWW_RAM.used_mem_perc}
:style {styles.mem}
)
)
(defwidget disk-indicator []
(asciibar
:value {EWW_DISK["/"].used_perc}
:style {styles.disk}
)
)
(defwidget disk []
(asciilabel
:text "${round(EWW_DISK['/'].used / 1024 / 1024 / 1024, 0)} ${round(EWW_DISK['/'].free / 1024 / 1024 / 1024, 0)} G"
:value {EWW_DISK['/'].used_perc}
:style {styles.disk}
)
)
(defwidget network-indicator []
(box
:orientation "vertical"
:space-evenly false
(box
:visible {ethernet.ip != ""}
:orientation "vertical"
(asciibin
:maxl {ethernet.quality / 8}
:maxr {ethernet.quality / 8}
:valuel {round(ethernet.up_speed / 1000 / 1000 / 8, 0) ?: 0}
:valuer {round(ethernet.down_speed / 1000 / 1000 / 8, 0) ?: 0}
)
(label :text ' ')
(label :text ' ')
)
(box
:visible {wifi.ip != ""}
:orientation "vertical"
(asciibar
:text {wifi.quality == 0 ? ' ' : wifi.ip == '' ? '──' : ''}
:value {wifi.quality}
:style {styles.wifi}
)
(label :text ' ')
(label :text ' ')
)
)
)
(defwidget network []
(box
:style "padding: 0px 8px;"
:orientation "vertical"
(box
:orientation "vertical"
:space-evenly false
:visible {ethernet.ip != ""}
(label
:xalign 0
:show-truncated false
:text "${ethernet.ip} ${ethernet.quality}M"
)
(label
:xalign 0
:show-truncated false
:style "color: #666;"
:text {ethernet.up_speed > 0 ? "▲ ${ethernet.up}" : ""}
)
(label
:xalign 0
:show-truncated false
:style "color: #666;"
:text {ethernet.down_speed > 0 ? "▼ ${ethernet.down}" : ""}
)
)
(box
:orientation "vertical"
:space-evenly false
:visible {wifi.ip != ""}
(asciilabel
:text "${wifi.ip} ${wifi.ssid}"
:value {wifi.quality}
:style {styles.wifi}
)
(label
:xalign 0
:show-truncated false
:style "color: #666;"
:text {wifi.up_speed > 0 ? "▲ ${wifi.up}" : ""}
)
(label
:xalign 0
:show-truncated false
:style "color: #666;"
:text {wifi.down_speed > 0 ? "▼ ${wifi.down}" : ""}
)
)
)
)
(defwidget bluetooth-indicator []
(box
:orientation "vertical"
(for device in bluetooth
(eventbox
:style "color: ${device.connected ? '#0099cc' : '#666666'};"
:onclick "bluetoothctl ${device.connected ? 'disconnect' : 'connect'} ${device.mac}"
:cursor "pointer"
(box
:spacing 5
(asciibar :value {device.battery != "N/A" ? "${device.battery}" : "0"})
)
)
)
)
)
(defwidget bluetooth []
(box
:style "padding: 0px 8px;"
:orientation "vertical"
(for device in bluetooth
(eventbox
:style "color: ${device.connected ? '#0099cc' : '#666666'};"
:onclick "bluetoothctl ${device.connected ? 'disconnect' : 'connect'} ${device.mac}"
:cursor "pointer"
(box
(label :show-truncated false :xalign 0 :text {device.name})
)
)
)
)
)
(defwidget moon []
(label
:text moon
)
)
(defwidget volu []
(eventbox
:onclick "amixer set Master toggle"
:cursor "pointer"
(circular-progress
:style "color: ${ sound.muted ? '#444' : '#99cc99' };"
:height 6
:thickness 1
:value { sound.volume }
)
)
)
(defwidget idle []
(eventbox
:style "color: ${idle-inhibited == 'on' ? '#ffcc66' : '#666'};"
:onclick "matcha -t"
:cursor "pointer"
(box
(label :text {idle-inhibited == "on" ? "◯" : "◠◡" })
)
)
)
(defwidget char []
(eventbox
(circular-progress
:style "color: #527386;"
:clockwise false
:height 6
:thickness { EWW_BATTERY == '' ? 3 : 1 }
:value 30
:value {EWW_BATTERY == '' ? 100 : EWW_BATTERY?.BAT0?.capacity ?: EWW_BATTERY?.BAT1?.capacity ?: EWW_BATTERY?.BAT?.capacity ?: 100}
)
)
)
(defwidget tray []
(systray
:orientation "vertical"
:icon-size 12
:spacing 15
:prepend-new false
)
)
(defwidget spaces []
(box
:orientation "vertical"
(for w in spaces
(eventbox
:class "space ${w.is_urgent ? 'urgent' : ''}"
:onclick "niri msg action focus-workspace ${w.idx}"
:cursor "pointer"
(label
:text { w.is_focused ? '●' : w.is_active ? '◍' : w.active_window_id == 'null' ? '◌' : '○' }
)
)
)
)
)
(defwidget asciibar [value ?min ?max ?style ?text]
(label
:class "asciibar"
:unindent false
:style { style == "" ? "" : "${style[round(((value - (min ?: 0)) / ((max ?: 100) - (min ?: 0))) * (arraylength(style) - 1), 0)]}" }
:text { text ?: asciibars[round(((value - (min ?: 0)) / ((max ?: 100) - (min ?: 0))) * (arraylength(asciibars) - 1), 0)] }
)
)
(defwidget asciibin [valuel ?minl ?maxl valuer ?minr ?maxr]
(label
:class "asciibin"
:unindent false
:text "${
(maxl ?: 100) == (minl ?: 0)
? asciibins[0]
: asciibins[round(((valuel - (minl ?: 0)) / ((maxl ?: 100) - (minl ?: 0))) * (arraylength(asciibins) - 1), 0)]
}${
(maxr ?: 100) == (minr ?: 0)
? asciibins[0]
: asciibins[round(((valuer - (minr ?: 0)) / ((maxr ?: 100) - (minr ?: 0))) * (arraylength(asciibins) - 1), 0)]
}"
)
)
(defwidget asciilabel [value ?min ?max ?style text]
(label
:xalign 0
:unindent false
:show-truncated false
:style { style == "" ? "" : "${style[round(((value - (min ?: 0)) / ((max ?: 100) - (min ?: 0))) * (arraylength(style) - 1), 0)]}" }
:text { text }
)
)
; io
(defvar asciibars
'[" ", "▏ ", "▎ ", "▍ ", "▌ ", "▋ ", "▊ ", "▉ ", "█ ", "█▏", "█▎", "█▍", "█▌", "█▋", "█▊", "█▉", "██"]'
)
(defvar asciibins
'["█", "▝", "▐", "▖", "▄", "▞", "▟", "▘", "▚", "▀", "▜", "▌", "▙", "▛"]'
)
(defvar styles
'{
"charge": [
"color: #992222; opacity: 1.0;",
"color: #996666; opacity: 1.0;",
"color: #996666; opacity: 1.0;",
"color: #996666; opacity: 1.0;",
"color: #527386; opacity: 1.0;",
"color: #527386; opacity: 1.0;",
"color: #527386; opacity: 1.0;",
"color: #527386; opacity: 1.0;",
"color: #527386; opacity: 1.0;",
"color: #527386; opacity: 1.0;",
"color: #527386; opacity: 1.0;",
"color: #527386; opacity: 1.0;",
"color: #527386; opacity: 1.0;",
"color: #527386; opacity: 1.0;",
"color: #527386; opacity: 1.0;",
"color: #527386; opacity: 1.0;"
],
"cpu": [
"color: #222222; opacity: 0.0;",
"color: #222222; opacity: 0.0;",
"color: #747369; opacity: 0.2;",
"color: #747369; opacity: 0.2;",
"color: #747369; opacity: 0.4;",
"color: #747369; opacity: 0.4;",
"color: #99cc99; opacity: 0.4;",
"color: #99cc99; opacity: 0.4;",
"color: #99cc99; opacity: 0.6;",
"color: #99cc99; opacity: 0.6;",
"color: #99cc99; opacity: 0.8;",
"color: #99cc99; opacity: 0.8;",
"color: #99cc99; opacity: 1.0;",
"color: #99cc99; opacity: 1.0;",
"color: #cc9966; opacity: 1.0;",
"color: #cc9966; opacity: 1.0;"
],
"disk": [
"color: #ffffff; opacity: 0.0;",
"color: #ffffff; opacity: 0.0;",
"color: #ffffff; opacity: 0.2;",
"color: #ffffff; opacity: 0.2;",
"color: #ffffff; opacity: 0.4;",
"color: #ffffff; opacity: 0.4;",
"color: #ffffff; opacity: 0.6;",
"color: #ffffff; opacity: 0.6;",
"color: #ffffff; opacity: 0.67;",
"color: #ffffff; opacity: 0.67;",
"color: #ffffff; opacity: 0.8;",
"color: #ffffff; opacity: 0.8;",
"color: #ffffff; opacity: 1.0;",
"color: #ffffff; opacity: 1.0;",
"color: #c47848; opacity: 1.0;",
"color: #c47848; opacity: 1.0;"
],
"mem": [
"color: #846488; opacity: 0.4;",
"color: #846488; opacity: 0.4;",
"color: #846488; opacity: 0.6;",
"color: #846488; opacity: 0.6;",
"color: #846488; opacity: 0.8;",
"color: #846488; opacity: 0.8;",
"color: #846488; opacity: 1.0;",
"color: #846488; opacity: 1.0;",
"color: #cc99cc; opacity: 0.8;",
"color: #cc99cc; opacity: 0.8;",
"color: #cc99cc; opacity: 0.8;",
"color: #cc99cc; opacity: 1.0;",
"color: #cc99cc; opacity: 1.0;",
"color: #cc99cc; opacity: 1.0;",
"color: #ffccff; opacity: 1.0;",
"color: #ffffff; opacity: 1.0;"
],
"wifi": [
"color: #ff0000; opacity: 1.0;",
"color: #ff0000; opacity: 1.0;",
"color: #ff6600; opacity: 1.0;",
"color: #ff6600; opacity: 1.0;",
"color: #ff9900; opacity: 1.0;",
"color: #ff9900; opacity: 1.0;",
"color: #ffae00; opacity: 1.0;",
"color: #ffae00; opacity: 1.0;",
"color: #ffdd00; opacity: 1.0;",
"color: #ffdd00; opacity: 1.0;",
"color: #ddff00; opacity: 1.0;",
"color: #ddff00; opacity: 1.0;",
"color: #aeff00; opacity: 1.0;",
"color: #aeff00; opacity: 1.0;",
"color: #00ff00; opacity: 1.0;",
"color: #00ff00; opacity: 1.0;"
]
}'
)
(defpoll date :interval "1s"
:initial '{"hour" : "XX", "minute" : "XX", "day" : "XX", "month" : "XX"}'
`date +'{"hour" : "%H", "minute" : "%M", "day" : "%d", "month" : "%m"}'`
)
(defpoll moon :interval "1h"
:initial "🌕︎"
`# Synodic month in seconds (29.530588 days)
SYNODIC_SECONDS=2551443
# Reference date when moon was at known phase
# Jan 15 2023 was third quarter (75%)
REF_EPOCH=$(date --date "Jan 15 2023" +%s)
REF_PERCENT=75
# Get current time in seconds since epoch
NOW_EPOCH=$(date +%s)
# Calculate time difference
SEC_DIFF=$((NOW_EPOCH - REF_EPOCH))
# Convert to hundredths of synodic months (centi-moons)
CENTI_MOONS=$((100 * SEC_DIFF / SYNODIC_SECONDS))
# Calculate moon phase percentage (0-99)
MOON_PERCENT=$(((CENTI_MOONS + REF_PERCENT) % 100))
# Handle negative percentages
if [ $MOON_PERCENT -lt 0 ]; then
MOON_PERCENT=$((MOON_PERCENT + 100))
fi
# Map percentage to moon phase emoji
# 0% = new moon, 50% = full moon
if [ $MOON_PERCENT -lt 7 ]; then
echo "🌕︎" # new
elif [ $MOON_PERCENT -lt 19 ]; then
echo "🌖︎" # waxing crescent
elif [ $MOON_PERCENT -lt 31 ]; then
echo "🌗︎" # first quarter
elif [ $MOON_PERCENT -lt 44 ]; then
echo "🌘︎" # waxing gibbous
elif [ $MOON_PERCENT -lt 57 ]; then
echo "🌑︎" # full
elif [ $MOON_PERCENT -lt 69 ]; then
echo "🌒︎" # waning gibbous
elif [ $MOON_PERCENT -lt 81 ]; then
echo "🌓︎" # last quarter
else
echo "🌔︎" # waning crescent
fi`
)
(defpoll notifications :interval "3s"
:initial ""
`makoctl history | grep "^Notification" | sed 's/^Notification [0-9]*: //'`
)
;; Calendar with highlighted current day
(defpoll cal :interval "1m"
`cal -mv --color=always | sed -e 's/\\x1b\\[7m/<span background="#999" foreground="#222">/g' -e 's/\\x1b\\[0m/<\\/span>/g'`
)
(defpoll clipboard :interval "1s"
:initial ""
`wl-paste`
)
(defpoll idle-inhibited :interval "500ms"
`matcha -s`
)
(defpoll ethernet :interval "3s"
:initial '{"ip":"","down":"0B/s","up":"0B/s","down_speed":0,"up_speed":0,"quality":0}'
`iface=$(ls /sys/class/net/ 2>/dev/null | grep -E '^(eth|enp|eno|ens)' | head -1)
[ -z "\$iface" ] && echo '{"ip":"","down":"0B/s","up":"0B/s","down_speed":0,"up_speed":0,"quality":0}' && exit
[ ! -d "/sys/class/net/\$iface" ] && echo '{"ip":"","down":"0B/s","up":"0B/s","down_speed":0,"up_speed":0,"quality":0}' && exit
ip=$(ip -4 addr show \$iface 2>/dev/null | grep -oP '(?<=inet\\s)\\d+(\\.\\d+){3}' | head -n 1)
speed=$(cat /sys/class/net/\$iface/speed 2>/dev/null || echo 0)
rx=$(cat /sys/class/net/\$iface/statistics/rx_bytes)
tx=$(cat /sys/class/net/\$iface/statistics/tx_bytes)
cache=/tmp/eth_\$iface
if [ -f \$cache ]; then
read rx_p tx_p < \$cache
rx_rate=$(((rx-rx_p)/3))
tx_rate=$(((tx-tx_p)/3))
else
rx_rate=0 tx_rate=0
fi
echo "\$rx \$tx" > \$cache
down=$(awk -v b=\$rx_rate 'BEGIN{u[0]="B/s";u[1]="KB/s";u[2]="MB/s";u[3]="GB/s";for(i=0;b>=1024&&i<3;i++)b/=1024;printf(i==0?"%d%s":"%.1f%s",b,u[i])}')
up=$(awk -v b=\$tx_rate 'BEGIN{u[0]="B/s";u[1]="KB/s";u[2]="MB/s";u[3]="GB/s";for(i=0;b>=1024&&i<3;i++)b/=1024;printf(i==0?"%d%s":"%.1f%s",b,u[i])}')
printf '{"ip":"%s","down":"%s","up":"%s","down_speed":%s,"up_speed":%s,"quality":%s}' "\$ip" "\$down" "\$up" \$rx_rate \$tx_rate \$speed`
)
(defpoll wifi :interval "3s"
:initial '{"ip":"","ssid":"","down":"0B/s","up":"0B/s","down_speed":0,"up_speed":0,"quality":0}'
`iface=$(ls /sys/class/net/ 2>/dev/null | grep -E '^(wlan|wlp)' | head -1)
[ -z "\$iface" ] && echo '{"ip":"","ssid":"","down":"0B/s","up":"0B/s","down_speed":0,"up_speed":0,"quality":0}' && exit
[ ! -d "/sys/class/net/\$iface" ] && echo '{"ip":"","ssid":"","down":"0B/s","up":"0B/s","down_speed":0,"up_speed":0,"quality":0}' && exit
ip=$(ip -4 addr show \$iface 2>/dev/null | grep -oP '(?<=inet\\s)\\d+(\\.\\d+){3}' | head -n 1)
ssid=$(iw dev \$iface link 2>/dev/null | awk '/SSID/{for(i=2;i<=NF;i++)printf "%s%s",\$i,(i<NF?" ":""); print ""}')
signal_dbm=""
wifi_link=$(iw dev \$iface link 2>/dev/null)
if echo "\$wifi_link" | grep -q "signal:"; then
signal_dbm=$(echo "\$wifi_link" | awk '/signal/{print \$2}')
fi
if [ -z "\$signal_dbm" ] && [ -z "\$signal" ] && command -v iwctl >/dev/null 2>&1; then
iwd_link=$(iwctl station \$iface show 2>/dev/null)
iwd_rssi=$(echo "\$iwd_link" | grep -oP 'RSSI\s+\K-?[0-9]+')
if [ -n "\$iwd_rssi" ]; then
signal_dbm=\$iwd_rssi
fi
fi
if [ -z "\$signal_dbm" ] && command -v nmcli >/dev/null 2>&1; then
nmcli_signal=$(nmcli -t -f DEVICE,SIGNAL dev wifi 2>/dev/null | grep "^\$iface:" | cut -d':' -f2)
if [ -n "\$nmcli_signal" ]; then
signal=\$nmcli_signal
fi
fi
if [ -n "\$signal_dbm" ]; then
signal=$((2 * (signal_dbm + 100)))
[ \$signal -lt 0 ] && signal=0
[ \$signal -gt 100 ] && signal=100
elif [ -z "\$signal" ]; then
signal=0
fi
rx=$(cat /sys/class/net/\$iface/statistics/rx_bytes 2>/dev/null || echo 0)
tx=$(cat /sys/class/net/\$iface/statistics/tx_bytes 2>/dev/null || echo 0)
pf="/tmp/wifi_\$iface"
if [ -f "\$pf" ]; then
read pr pt < "\$pf"
rs=$(((rx-pr)/3))
ts=$(((tx-pt)/3))
else
rs=0
ts=0
fi
echo "\$rx \$tx" > "\$pf"
rh=$(awk -v b=\$rs 'BEGIN{if(b<1024)printf"%dB/s",b;else if(b<1048576)printf"%.1fK/s",b/1024;else if(b<1073741824)printf"%.1fM/s",b/1048576;else printf"%.1fG/s",b/1073741824}')
th=$(awk -v b=\$ts 'BEGIN{if(b<1024)printf"%dB/s",b;else if(b<1048576)printf"%.1fK/s",b/1024;else if(b<1073741824)printf"%.1fM/s",b/1048576;else printf"%.1fG/s",b/1073741824}')
printf '{"ip":"%s","ssid":"%s","down":"%s","up":"%s","down_speed":%d,"up_speed":%d,"quality":%d}' "\$ip" "\$ssid" "\$rh" "\$th" "\$rs" "\$ts" "\$signal"`
)
(defpoll reco :interval "300ms"
`pgrep -x wf-recorder > /dev/null && echo true || echo false`
)
(deflisten bluetooth
:initial "[]"
`#!/bin/bash
update_devices() {
{
bluetoothctl devices | awk '{print \$2}' | while read -r mac; do
info=\$(bluetoothctl info "\$mac")
name=\$(echo "\$info" | sed -n 's/.*Name: //p')
connected=\$(echo "\$info" | grep -q "Connected: yes" && echo "true" || echo "false")
battery=\$(echo "\$info" | awk '/Battery Percentage/{print \$4}' | tr -d '()')
[ -z "\$battery" ] && battery="0"
jq -nc --arg name "\$name" --arg mac "\$mac" --arg battery "\$battery" --argjson connected "\$connected" \
'{name: \$name, battery: (\$battery | tonumber), connected: \$connected, mac: \$mac}'
done
} | jq -sc 'sort_by(.connected) | reverse'
}
update_devices
dbus-monitor --system "type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',arg0='org.bluez.Device1'" 2>/dev/null | \
grep --line-buffered -E "(Connected|Percentage)" | while read -r line; do
sleep 0.3
update_devices
done`
)
(deflisten spaces
:initial "[]"
`niri msg -j workspaces && niri msg -j event-stream | while read -r line; \
do niri msg -j workspaces | jq -c 'sort_by(.idx)'; \
done`
)
(deflisten sound
:initial '{"volume": 0, "muted": false, "bluetooth": false, "device": "Unknown"}'
`device=$(wpctl inspect @DEFAULT_AUDIO_SINK@ | sed -n 's/.*node\\.description = "\\(.*\\)".*/\\1/p');
wpctl get-volume @DEFAULT_AUDIO_SINK@ | awk -v dev="$device" '{
vol = int($2*100);
muted = /MUTED/ ? "true" : "false";
printf "{\\\"volume\\\": %d, \\\"muted\\\": %s, \\\"bluetooth\\\": false, \\\"device\\\": \\\"%s\\\"}\\n", vol, muted, dev;
}';
pw-dump --monitor 2>/dev/null | grep --line-buffered "\\"Props\\"" | while read -r line; do
device=$(wpctl inspect @DEFAULT_AUDIO_SINK@ | sed -n 's/.*node\\.description = "\\(.*\\)".*/\\1/p');
wpctl get-volume @DEFAULT_AUDIO_SINK@ | awk -v dev="$device" '{
vol = int($2*100);
muted = /MUTED/ ? "true" : "false";
printf "{\\\"volume\\\": %d, \\\"muted\\\": %s, \\\"bluetooth\\\": false, \\\"device\\\": \\\"%s\\\"}\\n", vol, muted, dev;
}';
done`
)
(deflisten player
:initial '{"playerName": "", "status": "", "title": "", "artist": "", "album": ""}'
`playerctl -F metadata -f '{"playerName": "{{playerName}}", "status": "{{status}}", "title": "{{title}}", "artist": "{{artist}}", "album": "{{album}}"}'`
)