diff options
Diffstat (limited to 'home/config/i3/bar/i3status.go')
| -rw-r--r-- | home/config/i3/bar/i3status.go | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/home/config/i3/bar/i3status.go b/home/config/i3/bar/i3status.go new file mode 100644 index 0000000..4627734 --- /dev/null +++ b/home/config/i3/bar/i3status.go @@ -0,0 +1,237 @@ +package main + +import ( + "io" + "net/http" + "os" + "regexp" + "strconv" + "time" + + "barista.run" + "barista.run/bar" + "barista.run/colors" + "barista.run/modules/battery" + "barista.run/modules/clock" + "barista.run/modules/diskspace" + "barista.run/modules/funcs" + "barista.run/modules/netinfo" + "barista.run/modules/volume" + "barista.run/modules/volume/pulseaudio" + "barista.run/modules/wlan" + "barista.run/outputs" + "barista.run/pango" + "barista.run/pango/icons/mdi" +) + +func main() { + // Constants + colors.LoadFromMap(map[string]string{ + // Color palette of Cezanne's Vue de la Baie de Marseille + "good": "#C5D294", + "degraded": "#E9CC67", + "bad": "#FFBC88", + }) + mdi.Load() // repo path will be inserted at build time + + // Display space left on / + storageIcon := pango.Icon("mdi-database") + barista.Add(diskspace.New("/").Output(func(i diskspace.Info) bar.Output { + used := i.UsedPct() + var colorScheme string + if used >= 90 { + colorScheme = "bad" + } else if used >= 50 { + colorScheme = "degraded" + } else { + colorScheme = "good" + } + return outputs.Pango(storageIcon, pango.Textf(" %d%%", used)).Color(colors.Scheme(colorScheme)) + })) + + // Check connection to the Mullvad VPN + mullvadIsUpRe := regexp.MustCompile(`^You are connected to Mullvad`) + mullvadServerRe := regexp.MustCompile(`\(server (.*)\)`) + mullvadIpRe := regexp.MustCompile(`Your IP address is (.*)`) + client := &http.Client{Timeout: 3 * time.Second} + incognitoIcon := pango.Icon("mdi-incognito") + incognitoOffIcon := pango.Icon("mdi-incognito-off") + barista.Add(funcs.Every(5*time.Second, func(s bar.Sink) { + icon := incognitoOffIcon + message := pango.Text("") + colorScheme := "bad" + res, err := client.Get("https://am.i.mullvad.net/connected") + if !s.Error(err) { + status, err := io.ReadAll(res.Body) + res.Body.Close() + if !s.Error(err) { + var re *regexp.Regexp + if mullvadIsUpRe.Match(status) { + re = mullvadServerRe + colorScheme = "good" + icon = incognitoIcon + } else { + re = mullvadIpRe + colorScheme = "degraded" + } + result := re.FindSubmatch(status) + if len(result) >= 2 { + message = pango.Textf(" %s", result[1]) + } + } + } + client.CloseIdleConnections() + s.Output(outputs.Pango(icon, message).Color(colors.Scheme(colorScheme))) + })) + + // Display the wifi status + wifiOffIcon := pango.Icon("mdi-wifi-off") + wifiRefreshIcon := pango.Icon("mdi-wifi-refresh") + wifiOnIcon := pango.Icon("mdi-wifi") + barista.Add(wlan.Named("wlp2s0").Output(func(w wlan.Info) bar.Output { + switch { + case w.Connected(): + return outputs.Pango(wifiOnIcon, pango.Textf(" %s", w.SSID)).Color(colors.Scheme("good")) + case w.Connecting(): + return outputs.Pango(wifiRefreshIcon).Color(colors.Scheme("degraded")) + case w.Enabled(): + return outputs.Pango(wifiOffIcon).Color(colors.Scheme("bad")) + default: + return nil + } + })) + + // Display the ethernet status + ethernetCableOnIcon := pango.Icon("mdi-ethernet-cable") + ethernetCableOffIcon := pango.Icon("mdi-ethernet-cable-off") + barista.Add(netinfo.Prefix("e").Output(func(s netinfo.State) bar.Output { + switch { + case s.Connected(): + ip := "<no ip>" + if len(s.IPs) > 0 { + ip = s.IPs[0].String() + } + return outputs.Pango(ethernetCableOnIcon, pango.Textf(" %s", ip)).Color(colors.Scheme("good")) + case s.Connecting(): + return outputs.Pango(ethernetCableOnIcon).Color(colors.Scheme("degraded")) + case s.Enabled(): + return outputs.Pango(ethernetCableOffIcon).Color(colors.Scheme("bad")) + default: + return nil + } + })) + + // Display the battery status + batteryIcons := [11]*pango.Node{pango.Icon("mdi-battery-outline"), + pango.Icon("mdi-battery-10"), + pango.Icon("mdi-battery-20"), + pango.Icon("mdi-battery-30"), + pango.Icon("mdi-battery-40"), + pango.Icon("mdi-battery-50"), + pango.Icon("mdi-battery-60"), + pango.Icon("mdi-battery-70"), + pango.Icon("mdi-battery-80"), + pango.Icon("mdi-battery-90"), + pango.Icon("mdi-battery")} + batteryChargingIcons := [11]*pango.Node{pango.Icon("mdi-battery-charging-outline"), + pango.Icon("mdi-battery-charging-10"), + pango.Icon("mdi-battery-charging-20"), + pango.Icon("mdi-battery-charging-30"), + pango.Icon("mdi-battery-charging-40"), + pango.Icon("mdi-battery-charging-50"), + pango.Icon("mdi-battery-charging-60"), + pango.Icon("mdi-battery-charging-70"), + pango.Icon("mdi-battery-charging-80"), + pango.Icon("mdi-battery-charging-90"), + pango.Icon("mdi-battery-charging-100")} + barista.Add(battery.All().Output(func(b battery.Info) bar.Output { + switch b.Status { + case battery.Disconnected, battery.Unknown: + return nil + default: + var icons [11]*pango.Node + var colorScheme string + if b.Status == battery.Charging { + icons = batteryChargingIcons + colorScheme = "good" + } else { + icons = batteryIcons + if b.RemainingPct() <= 10 { + colorScheme = "bad" + } else if b.RemainingPct() <= 20 { + colorScheme = "degraded" + } else { + colorScheme = "good" + } + } + icon := icons[b.RemainingPct()/10] + return outputs.Pango(icon, pango.Textf(" %d%%", b.RemainingPct())).Color(colors.Scheme(colorScheme)) + } + })) + + // Display brightness + brightnessHighIcon := pango.Icon("mdi-lightbulb-on") + brightnessMidIcon := pango.Icon("mdi-lightbulb-on-outline") + brightnessLowIcon := pango.Icon("mdi-lightbulb-outline") + ReadBrightness := func(name string) (int, error) { + valueStr, err := os.ReadFile("/sys/class/backlight/intel_backlight/" + name) + if err != nil { + return 0, err + } + return strconv.Atoi(string(valueStr[:len(valueStr)-1])) + } + brightnessMax, _ := ReadBrightness("max_brightness") // always non-zero, unless there's an error + barista.Add(funcs.Every(time.Second, func(s bar.Sink) { + brightness, err := ReadBrightness("brightness") + if !s.Error(err) { + value := (brightness * 100) / brightnessMax + var icon *pango.Node + if value <= 30 { + icon = brightnessLowIcon + } else if value < 70 { + icon = brightnessMidIcon + } else { + icon = brightnessHighIcon + } + s.Output(outputs.Pango(icon, pango.Textf(" %d%%", value))) + } + })) + + // Display output volume + volumeOffIcon := pango.Icon("mdi-volume-variant-off") + volumeLowIcon := pango.Icon("mdi-volume-low") + volumeMidIcon := pango.Icon("mdi-volume-medium") + volumeHighIcon := pango.Icon("mdi-volume-high") + barista.Add(volume.New(pulseaudio.DefaultSink()).Output(func(v volume.Volume) bar.Output { + volume := v.Pct() + var icon *pango.Node + if volume == 0 || v.Mute { + icon = volumeOffIcon + } else if volume <= 30 { + icon = volumeLowIcon + } else if volume <= 70 { + icon = volumeMidIcon + } else { + icon = volumeHighIcon + } + return outputs.Pango(icon, pango.Textf(" %d%%", volume)) + })) + + // Display microphone volume + microphoneOffIcon := pango.Icon("mdi-microphone-off") + microphoneIcon := pango.Icon("mdi-microphone") + barista.Add(volume.New(pulseaudio.DefaultSource()).Output(func(v volume.Volume) bar.Output { + volume := v.Pct() / 10 // TODO: correct Pct implementation for sources + var icon *pango.Node + if volume == 0 || v.Mute { + icon = microphoneOffIcon + } else { + icon = microphoneIcon + } + return outputs.Pango(icon, pango.Textf(" %d%%", volume)) + })) + + barista.Add(clock.Local().OutputFormat("2006-01-02 15:04:05")) + + panic(barista.Run()) +} |
