From 213218d21a56d0acf92e2ae0a4c514af0e0747e8 Mon Sep 17 00:00:00 2001 From: Steins7 Date: Mon, 7 Nov 2022 20:49:15 +0100 Subject: [PATCH] Initial commit --- config | 148 +++++++++++++++++++++++++ scripts/batt_watts.py | 22 ++++ scripts/battery.py | 154 ++++++++++++++++++++++++++ style.css | 250 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 574 insertions(+) create mode 100644 config create mode 100755 scripts/batt_watts.py create mode 100755 scripts/battery.py create mode 100644 style.css diff --git a/config b/config new file mode 100644 index 0000000..1d8cfec --- /dev/null +++ b/config @@ -0,0 +1,148 @@ +{ + // "layer": "top", // Waybar at top layer + // "position": "bottom", // Waybar position (top|bottom|left|right) + "height": 25, // Waybar height (to be removed for auto height) + // "width": 1280, // Waybar width + // Choose the order of the modules + "modules-left": ["sway/workspaces", "sway/mode"], + "modules-center": ["sway/window"], + "modules-right": ["idle_inhibitor", "pulseaudio", "network", "cpu", "memory", "temperature", "custom/bat1", "clock", "tray"], + // Modules configuration + // "sway/workspaces": { + // "disable-scroll": true, + // "all-outputs": true, + // "format": "{name}: {icon}", + // "format-icons": { + // "1": "", + // "2": "", + // "3": "", + // "4": "", + // "5": "", + // "urgent": "", + // "focused": "", + // "default": "" + // } + // }, + "sway/mode": { + "format": "{}" + }, + "mpd": { + "format": "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ⸨{songPosition}|{queueLength}⸩ ", + "format-disconnected": "Disconnected ", + "format-stopped": "{consumeIcon}{randomIcon}{repeatIcon}{singleIcon}Stopped ", + "unknown-tag": "N/A", + "interval": 2, + "consume-icons": { + "on": " " + }, + "random-icons": { + "off": " ", + "on": " " + }, + "repeat-icons": { + "on": " " + }, + "single-icons": { + "on": "1 " + }, + "state-icons": { + "paused": "", + "playing": "" + }, + "tooltip-format": "MPD (connected)", + "tooltip-format-disconnected": "MPD (disconnected)" + }, + "idle_inhibitor": { + "format": "{icon}", + "format-icons": { + "activated": "", + "deactivated": "" + } + }, + "tray": { + // "icon-size": 21, + "spacing": 10 + }, + "clock": { + // "timezone": "America/New_York", + "tooltip-format": "{:%Y %B}\n{calendar}", + "format-alt": "{:%Y-%m-%d}" + }, + "cpu": { + "format": "{usage}% ", + "tooltip": false, + "interval": 1, + "states": { + "heavy_usage": 75, + "normal_usage": 10, + } + }, + "memory": { + "format": "{}% " + }, + "temperature": { + // "thermal-zone": 2, + "hwmon-path": "/sys/class/hwmon/hwmon4/temp1_input", + "critical-threshold": 90, + // "format-critical": "{temperatureC}°C {icon}", + "format": "{temperatureC}°C {icon}", + "format-icons": ["", "", ""] + }, + "backlight": { + // "device": "acpi_video1", + "format": "{percent}% {icon}", + "format-icons": ["", ""] + }, + //"battery": { + // "states": { + // // "good": 95, + // "warning": 30, + // "critical": 15 + // }, + // "format": "{power}W {capacity}% {icon}", + // "format-charging": "{capacity}% ", + // "format-plugged": "{capacity}% ", + // "format-alt": "{time} {icon}", + // "interval": 10, + // // "format-good": "", // An empty format will hide the module + // // "format-full": "", + // "format-icons": ["", "", "", "", ""] + //}, + //"battery#bat2": { + // "bat": "BAT2" + //}, + "custom/bat1": { + "exec": "~/.config/waybar/scripts/battery.py BAT1", + "return-type": "json", + "format": " {} {icon} ", + "format-icons": ["", "", "", "", ""], + "tooltip": false + }, + "network": { + // "interface": "wlp2*", // (Optional) To force the use of this interface + "format-wifi": "{essid} ({signalStrength}%) ", + "format-ethernet": "{ifname} ", + "format-linked": "{ifname} (No IP) ", + "format-disconnected": "Disconnected ⚠", + "format-alt": "{ipaddr}/{cidr}" + }, + "pulseaudio": { + // "scroll-step": 1, // %, can be a float + "format": "{volume}% {icon} {format_source}", + "format-bluetooth": "{volume}% {icon} {format_source}", + "format-bluetooth-muted": " {icon} {format_source}", + "format-muted": " {format_source}", + "format-source": "{volume}% ", + "format-source-muted": "", + "format-icons": { + "headphone": "", + "hands-free": "", + "headset": "", + "phone": "", + "portable": "", + "car": "", + "default": ["", "", ""] + }, + "on-click": "pavucontrol" + } +} diff --git a/scripts/batt_watts.py b/scripts/batt_watts.py new file mode 100755 index 0000000..037efa7 --- /dev/null +++ b/scripts/batt_watts.py @@ -0,0 +1,22 @@ +#!/usr/bin/python + +dir='/sys/class/power_supply/BAT1/' + +with open(dir + 'current_now', 'r') as f: + current = int(f.read()) / 1000000.0 +with open(dir + 'voltage_now', 'r') as f: + voltage = int(f.read()) / 1000000.0 +with open(dir + 'status', 'r') as f: + status = f.read().rstrip() + print(status) + + if status == "Charging" or status == "Full": + wattage = voltage * current + elif status == "Discharging": + wattage = -voltage * current + else: + wattage = 0 + +print('{0:.2f}V {1:.2f}A {2:.2f}W'.format(voltage, current, wattage)) +print('{0:.2f}W'.format(wattage)) +#return wattage diff --git a/scripts/battery.py b/scripts/battery.py new file mode 100755 index 0000000..7b4c5b5 --- /dev/null +++ b/scripts/battery.py @@ -0,0 +1,154 @@ +#!/usr/bin/python + +import json +import os +import sys + +from gi.repository import GLib +from pydbus import SystemBus + +bat_levels = { + 15: 'critical', + 30: 'warning' + } + +bat_states = { + 0: 'unknown', + 1: 'charging', + 2: 'discharging', + 3: 'empty', + 4: 'full', + 5: 'charge_pending', + 6: 'discharge_pending' + } + +CSS_CLASS_DISCONNECTED = 'disconnected' + +class BatteryWatcher: + loop = None + dbus = None + upower = None + bat_name = None + bat_obj_path = None + bat = None + bat_design_capacity = None + bat_percentage_of_design = None + bat_percentage_of_current = None + bat_state = None + bat_energy_rate = None + + def __init__(self, battery_name="BAT0"): + self.loop = GLib.MainLoop() + self.bat_name = battery_name + self.dbus = SystemBus() + self.upower = self.dbus.get('org.freedesktop.UPower', + '/org/freedesktop/UPower') + self.bat_obj_path = '/org/freedesktop/UPower/devices/battery_{}'\ + .format(self.bat_name) + + def run(self): + if self.bat_obj_path in self.upower.EnumerateDevices(): + self.upower.onDeviceRemoved = self.onDeviceRemoved + self.add_battery() + else: + self.upower.onDeviceAdded = self.onDeviceAdded + + self.export_state() + self.loop.run() + + def add_battery(self): + self.bat = self.dbus.get( + 'org.freedesktop.UPower', self.bat_obj_path) + + self.bat.onPropertiesChanged = self.onPropertiesChanged + + self.bat_design_capacity = self.bat.EnergyFullDesign + self.bat_percentage_of_design = \ + (self.bat.Energy / self.bat_design_capacity) * 100 + self.bat_percentage_of_current = self.bat.Percentage + self.bat_state = self.bat.State + self.bat_energy_rate = self.bat.EnergyRate + + def remove_battery(self): + self.bat.onPropertiesChanged = None + self.bat = None + + def export_state(self): + if self.bat: + percentage = int(round(self.bat_percentage_of_current)) + text = str(int(round(self.bat_percentage_of_design))) + "% (" \ + + str(int(round(self.bat_energy_rate, 2))) + "W)" + css_classes = [ bat_states[self.bat.State] ] + + for level in bat_levels.keys(): + if self.bat_percentage_of_design <= level: + css_classes.append(bat_levels[level]) + break + else: + percentage = 0 + text = "N/A" + css_classes = [ CSS_CLASS_DISCONNECTED ] + + output = { 'text': text, + 'percentage': percentage, + 'class' : css_classes } + + try: + print(json.dumps(output), flush=True) + except BrokenPipeError: + self.loop.quit() + + def onPropertiesChanged(self, interface_name, changed_properties, + invalidated_properties): + update = False + + if 'Energy' in changed_properties: + self.bat_percentage_of_design = \ + (changed_properties['Energy'] / self.bat_design_capacity) \ + * 100 + update = True + if 'Percentage' in changed_properties: + self.bat_percentage_of_current = changed_properties['Percentage'] + if 'State' in changed_properties: + self.bat_state = changed_properties['State'] + update = True + if 'EnergyRate' in changed_properties: + self.bat_energy_rate = changed_properties['EnergyRate'] + update = True + + if update: + self.export_state() + + def onDeviceAdded(self, object_path): + if object_path == self.bat_obj_path: + self.upower.onDeviceAdded = None + self.upower.onDeviceRemoved = self.onDeviceRemoved + self.add_battery() + self.export_state() + + def onDeviceRemoved(self, object_path): + if object_path == self.bat_obj_path: + self.upower.onDeviceAdded = self.onDeviceAdded + self.upower.onDeviceRemoved = None + self.remove_battery() + self.export_state() + +def main(argv=None): + battery_name = "BAT0" + + if len(argv) == 2: + battery_name = argv[1] + elif len(argv) > 2: + print("Error: Too many arguments!", file=sys.stderr) + return os.EX_USAGE + + try: + BatteryWatcher(battery_name).run() + except KeyboardInterrupt: + return 0 + except BrokenPipeError: + return 0 + +if __name__ == '__main__': + sys.exit(main(sys.argv)) + diff --git a/style.css b/style.css new file mode 100644 index 0000000..d2ee877 --- /dev/null +++ b/style.css @@ -0,0 +1,250 @@ +* { + /* `otf-font-awesome` is required to be installed for icons */ + font-family: FontAwesome, Roboto, Helvetica, Arial, sans-serif; + font-size: 14px; + min-height: 0; +} + +window#waybar { + background-color: rgba(43, 48, 59, 0.5); + border-bottom: 3px solid rgba(100, 114, 125, 0.5); + color: #ffffff; + transition-property: background-color; + transition-duration: .5s; +} + +window#waybar.hidden { + opacity: 0.2; +} + +/* +window#waybar.empty { + background-color: transparent; +} +window#waybar.solo { + background-color: #FFFFFF; +} +*/ + +#workspaces button { + padding: 0 5px; + background-color: transparent; + color: #ffffff; + /* Use box-shadow instead of border so the text isn't offset */ + box-shadow: inset 0 -3px transparent; + /* Avoid rounded borders under each workspace name */ + border: none; + border-radius: 0; +} + +/* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */ +#workspaces button:hover { + background: rgba(0, 0, 0, 0.2); + box-shadow: inset 0 -3px #ffffff; +} + +#workspaces button.focused { + background-color: #64727D; + box-shadow: inset 0 -3px #ffffff; +} + +#workspaces button.urgent { + background-color: #eb4d4b; +} + +#mode { + background-color: #64727D; + border-bottom: 3px solid #ffffff; +} + +#clock, +#battery, +#cpu, +#memory, +#disk, +#temperature, +#backlight, +#network, +#pulseaudio, +#custom-media, +#tray, +#mode, +#idle_inhibitor, +#mpd { + padding: 0 10px; + color: #ffffff; +} + +#window, +#workspaces { + margin: 0 4px; +} + +/* If workspaces is the leftmost module, omit left margin */ +.modules-left > widget:first-child > #workspaces { + margin-left: 0; +} + +/* If workspaces is the rightmost module, omit right margin */ +.modules-right > widget:last-child > #workspaces { + margin-right: 0; +} + +#clock { + color: #2980b9; +} + +#custom-bat0, +#custom-bat1, +#battery { + color: #2980b9; +} + +#custom-bat0.charging, +#custom-bat1.charging, +#battery.charging, #battery.plugged { + color: #26A65B; +} + +@keyframes blink { + to { + color: #ffffff; + } +} + +#custom-bat0.critical:not(.charging):not(.full), +#custom-bat1.critical:not(.charging):not(.full), +#battery.critical:not(.charging) { + background-color: #f53c3c; + animation-name: blink; + animation-duration: 0.5s; + animation-timing-function: linear; + animation-iteration-count: infinite; + animation-direction: alternate; +} + +label:focus { + background-color: #000000; +} + +#cpu { + color: #26A65B; + min-width: 50px; +} + +#cpu.normal_usage { + color: #f0932b; +} + +#cpu.heavy_usage { + color: #eb4d4b; +} + +#memory { + color: #9b59b6; +} + +#disk { + color: #964B00; +} + +#backlight { + color: #90b1b1; +} + +#network { + color: #2980b9; +} + +#network.disconnected { + color: #f53c3c; +} + +#pulseaudio { + color: #f1c40f; +} + +#pulseaudio.muted { + color: #90b1b1; + color: #2a5c45; +} + +#custom-media { + background-color: #66cc99; + color: #2a5c45; + min-width: 100px; +} + +#custom-media.custom-spotify { + background-color: #66cc99; +} + +#custom-media.custom-vlc { + background-color: #ffa000; +} + +#temperature { + color: #f0932b; +} + +#temperature.critical { + background-color: #eb4d4b; +} + +#tray { + background-color: #2980b9; +} + +#tray > .passive { + -gtk-icon-effect: dim; +} + +#tray > .needs-attention { + -gtk-icon-effect: highlight; + background-color: #eb4d4b; +} + +#idle_inhibitor.activated { + color: #eb4d4b; +} + +#mpd { + background-color: #66cc99; + color: #2a5c45; +} + +#mpd.disconnected { + background-color: #f53c3c; +} + +#mpd.stopped { + background-color: #90b1b1; +} + +#mpd.paused { + background-color: #51a37a; +} + +#language { + background: #00b093; + color: #740864; + padding: 0 5px; + margin: 0 5px; + min-width: 16px; +} + +#keyboard-state { + background: #97e1ad; + color: #000000; + padding: 0 0px; + margin: 0 5px; + min-width: 16px; +} + +#keyboard-state > label { + padding: 0 5px; +} + +#keyboard-state > label.locked { + background: rgba(0, 0, 0, 0.2); +}