From e842d34c9ae3a48a78bbcaa5b9fc58216e1d49a6 Mon Sep 17 00:00:00 2001 From: Toby Vincent Date: Mon, 7 Oct 2024 13:30:57 -0500 Subject: refactor(i3blocks): move i3blocks to bin dir --- i3blocks/.config/i3blocks/config | 22 ++-- i3blocks/.local/bin/i3blocks-cpu | 42 +++++++ i3blocks/.local/bin/i3blocks-disk | 28 +++++ i3blocks/.local/bin/i3blocks-dunst | 146 ++++++++++++++++++++++++ i3blocks/.local/bin/i3blocks-gpu | 30 +++++ i3blocks/.local/bin/i3blocks-memory | 30 +++++ i3blocks/.local/bin/i3blocks-miniflux | 39 +++++++ i3blocks/.local/bin/i3blocks-net | 54 +++++++++ i3blocks/.local/bin/i3blocks-notmuch | 33 ++++++ i3blocks/.local/bin/i3blocks-title | 17 +++ i3blocks/.local/bin/i3blocks-volume | 128 +++++++++++++++++++++ i3blocks/.local/bin/i3blocks-wttr | 152 +++++++++++++++++++++++++ i3blocks/.local/lib/i3blocks/i3blocks-cpu | 42 ------- i3blocks/.local/lib/i3blocks/i3blocks-disk | 28 ----- i3blocks/.local/lib/i3blocks/i3blocks-dunst | 146 ------------------------ i3blocks/.local/lib/i3blocks/i3blocks-gpu | 30 ----- i3blocks/.local/lib/i3blocks/i3blocks-memory | 30 ----- i3blocks/.local/lib/i3blocks/i3blocks-miniflux | 39 ------- i3blocks/.local/lib/i3blocks/i3blocks-net | 54 --------- i3blocks/.local/lib/i3blocks/i3blocks-notmuch | 33 ------ i3blocks/.local/lib/i3blocks/i3blocks-title | 17 --- i3blocks/.local/lib/i3blocks/i3blocks-volume | 128 --------------------- i3blocks/.local/lib/i3blocks/i3blocks-wttr | 152 ------------------------- 23 files changed, 710 insertions(+), 710 deletions(-) create mode 100755 i3blocks/.local/bin/i3blocks-cpu create mode 100755 i3blocks/.local/bin/i3blocks-disk create mode 100755 i3blocks/.local/bin/i3blocks-dunst create mode 100755 i3blocks/.local/bin/i3blocks-gpu create mode 100755 i3blocks/.local/bin/i3blocks-memory create mode 100755 i3blocks/.local/bin/i3blocks-miniflux create mode 100755 i3blocks/.local/bin/i3blocks-net create mode 100755 i3blocks/.local/bin/i3blocks-notmuch create mode 100755 i3blocks/.local/bin/i3blocks-title create mode 100755 i3blocks/.local/bin/i3blocks-volume create mode 100755 i3blocks/.local/bin/i3blocks-wttr delete mode 100755 i3blocks/.local/lib/i3blocks/i3blocks-cpu delete mode 100755 i3blocks/.local/lib/i3blocks/i3blocks-disk delete mode 100755 i3blocks/.local/lib/i3blocks/i3blocks-dunst delete mode 100755 i3blocks/.local/lib/i3blocks/i3blocks-gpu delete mode 100755 i3blocks/.local/lib/i3blocks/i3blocks-memory delete mode 100755 i3blocks/.local/lib/i3blocks/i3blocks-miniflux delete mode 100755 i3blocks/.local/lib/i3blocks/i3blocks-net delete mode 100755 i3blocks/.local/lib/i3blocks/i3blocks-notmuch delete mode 100755 i3blocks/.local/lib/i3blocks/i3blocks-title delete mode 100755 i3blocks/.local/lib/i3blocks/i3blocks-volume delete mode 100755 i3blocks/.local/lib/i3blocks/i3blocks-wttr (limited to 'i3blocks') diff --git a/i3blocks/.config/i3blocks/config b/i3blocks/.config/i3blocks/config index 6afdd1a..58bcdb0 100644 --- a/i3blocks/.config/i3blocks/config +++ b/i3blocks/.config/i3blocks/config @@ -1,39 +1,39 @@ [i3blocks] -command=$SCRIPT_DIR/i3blocks-title +command=i3blocks-title interval=persist format=json [weather] -command=$SCRIPT_DIR/i3blocks-wttr +command=i3blocks-wttr interval=600 [miniflux] -command=$SCRIPT_DIR/i3blocks-miniflux +command=i3blocks-miniflux interval=30 [notmuch] -command=$SCRIPT_DIR/i3blocks-notmuch +command=i3blocks-notmuch interval=5 [disk] -command=$SCRIPT_DIR/i3blocks-disk +command=i3blocks-disk interval=20 [memory] -command=$SCRIPT_DIR/i3blocks-memory +command=i3blocks-memory interval=5 [cpu] -command=$SCRIPT_DIR/i3blocks-cpu +command=i3blocks-cpu interval=5 format=json [gpu] -command=$SCRIPT_DIR/i3blocks-gpu +command=i3blocks-gpu interval=5 [net] -command=$SCRIPT_DIR/i3blocks-net +command=i3blocks-net interval=5 [playerctl] @@ -62,7 +62,7 @@ interval=persist format=json [volume] -command=$SCRIPT_DIR/i3blocks-volume +command=i3blocks-volume interval=persist format=json gui=pavucontrol @@ -72,6 +72,6 @@ command=date +" %a %m/%d %T " interval=1 [dunst] -command=$SCRIPT_DIR/i3blocks-dunst +command=i3blocks-dunst interval=persist format=json diff --git a/i3blocks/.local/bin/i3blocks-cpu b/i3blocks/.local/bin/i3blocks-cpu new file mode 100755 index 0000000..ddb1d68 --- /dev/null +++ b/i3blocks/.local/bin/i3blocks-cpu @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 + +import os +import json + +ICON = "\U000f0ee0" # 󰻠 +BLACK = f"#{os.environ.get("BASE16_COLOR_00_HEX")}" +RED = f"#{os.environ.get("BASE16_COLOR_08_HEX")}" +YELLOW = f"#{os.environ.get("BASE16_COLOR_0A_HEX")}" +AQUA = f"#{os.environ.get("BASE16_COLOR_0C_HEX")}" + + +def main(): + off = json.loads(os.environ.get("values", json.dumps([0 for i in range(10)]))) + + with open("/proc/stat", "r") as f: + val = [int(x) for x in f.readline().strip().split(" ")[2:]] + + if elapsed := sum(val + off): + usage = sum(val[0:3] + val[5:] + off[0:3] + off[5:]) / elapsed + else: + usage = 0 + + output = { + "full_text": " {} {:.0%} ".format(ICON, usage), + "values": [-x for x in val], + } + if usage > 0.9: + output["color"] = BLACK + output["background"] = RED + elif usage > 0.6: + output["color"] = BLACK + output["background"] = YELLOW + elif usage > 0.3: + output["color"] = BLACK + output["background"] = AQUA + + print(json.dumps(output, ensure_ascii=False), flush=True) + + +if __name__ == "__main__": + main() diff --git a/i3blocks/.local/bin/i3blocks-disk b/i3blocks/.local/bin/i3blocks-disk new file mode 100755 index 0000000..a98a40c --- /dev/null +++ b/i3blocks/.local/bin/i3blocks-disk @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 + +import os + +ICON = "\U000f02ca" # 󰋊 +GB = 1073741824 + + +def main(): + statvfs = os.statvfs("/") + avail = bytes = statvfs.f_bavail * statvfs.f_frsize + + for unit in ["B", "KB", "MB", "GB", "TB", "PB"]: + if avail < 1024.0 or unit == "PB": + break + avail /= 1024.0 + + print(f" {ICON} {round(avail)}{unit} \n") + if bytes < (20 * GB): + print(f"#{os.environ.get("BASE16_COLOR_00_HEX")}") + print(f"#{os.environ.get("BASE16_COLOR_08_HEX")}") + elif bytes < (10 * GB): + print(f"#{os.environ.get("BASE16_COLOR_00_HEX")}") + print(f"#{os.environ.get("BASE16_COLOR_0A_HEX")}") + + +if __name__ == "__main__": + main() diff --git a/i3blocks/.local/bin/i3blocks-dunst b/i3blocks/.local/bin/i3blocks-dunst new file mode 100755 index 0000000..df3d9d4 --- /dev/null +++ b/i3blocks/.local/bin/i3blocks-dunst @@ -0,0 +1,146 @@ +#!/usr/bin/env python3 + +import asyncio +import os +import sys +import json +import pprint + +from dbus_next.aio import MessageBus +import i3ipc +from i3ipc.aio import Connection + + +BUS_NAME = "org.freedesktop.Notifications" +OBJECT_PATH = "/org/freedesktop/Notifications" +DUNST_INTERFACE = "org.dunstproject.cmd0" +PROPERTIES_INTERFACE = "org.freedesktop.DBus.Properties" +I3_EVENTS = [ + i3ipc.Event.WINDOW_FOCUS, + i3ipc.Event.WINDOW_FULLSCREEN_MODE, + i3ipc.Event.WINDOW_CLOSE, + i3ipc.Event.WORKSPACE_FOCUS, +] + + +class Dunst: + @classmethod + async def connect(cls): + self = cls() + self.bus = await MessageBus().connect() + + self.obj = self.bus.get_proxy_object( + BUS_NAME, + OBJECT_PATH, + await self.bus.introspect(BUS_NAME, OBJECT_PATH), + ) + + self.dunst = self.obj.get_interface(DUNST_INTERFACE) + self.paused = await self.dunst.get_paused() + self.displayed = await self.dunst.get_displayed_length() + self.waiting = await self.dunst.get_waiting_length() + + self.i3 = await Connection().connect() + + for event in I3_EVENTS: + self.i3.on(event, self.handle_i3_event) + + return self + + async def handle_i3_event(self, conn, e): + if e.change == "close": + enable = 0 + elif isinstance(e, i3ipc.WorkspaceEvent): + enable = e.current.fullscreen_mode + else: + enable = e.container.fullscreen_mode + + await self.dunst.call_rule_enable("transient_skip", enable) + + + def print_status(self): + if self.paused: + icon = "\U000f009b" # 󰂛 + else: + icon = "\U000f009a" # 󰂚 + + if self.waiting > 0: + count = f" {self.waiting}" + else: + count = "" + + output = {"full_text": f" {icon}{count} "} + + if self.displayed + self.waiting > 0: + output["color"] = f"#{os.environ.get("BASE16_COLOR_00_HEX")}" + output["background"] = f"#{os.environ.get("BASE16_COLOR_0C_HEX")}" + elif self.paused: + output["color"] = f"#{os.environ.get("BASE16_COLOR_00_HEX")}" + output["background"] = f"#{os.environ.get("BASE16_COLOR_0A_HEX")}" + + print(json.dumps(output, ensure_ascii=False), flush=True) + + def on_properties_changed(self, _name, property, invalidated): + update = False + for property, variant in property.items(): + match property: + case "paused": + self.paused = variant.value + case "displayedLength": + self.displayed = variant.value + case "waitingLength": + self.waiting = variant.value + case _: + continue + update = True + + if update: + self.print_status() + + async def listener(self): + properties = self.obj.get_interface(PROPERTIES_INTERFACE) + properties.on_properties_changed(self.on_properties_changed) + + await self.bus.wait_for_disconnect() + + async def button_handler(self): + loop = asyncio.get_event_loop() + reader = asyncio.StreamReader() + protocol = asyncio.StreamReaderProtocol(reader) + await loop.connect_read_pipe(lambda: protocol, sys.stdin) + + while True: + line = await reader.readline() + + if not line: + await asyncio.sleep(1) + continue + + match json.loads(line): + case {"button": 1}: + await self.dunst.call_notification_show() + case {"button": 2}: + pass + case {"button": 3}: + await self.dunst.set_paused(not self.paused) + case {"button": 4}: + pass + case {"button": 5}: + pass + + +async def main(): + dunst = await Dunst.connect() + dunst.print_status() + + try: + async with asyncio.TaskGroup() as task_group: + task_group.create_task(dunst.i3.main()) + task_group.create_task(dunst.listener()) + task_group.create_task(dunst.button_handler()) + except asyncio.CancelledError: + return + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/i3blocks/.local/bin/i3blocks-gpu b/i3blocks/.local/bin/i3blocks-gpu new file mode 100755 index 0000000..df96d6c --- /dev/null +++ b/i3blocks/.local/bin/i3blocks-gpu @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 + +import os +import glob + +ICON = "\U000f0379" # 󰍹 + + +def main(): + files = glob.glob("/sys/class/drm/card*/device/gpu_busy_percent") + if not files: + return + + with open(files[0]) as f: + load = int(f.readline()) + + print(f" {ICON} {load}% \n") + if load > 90: + print(f"#{os.environ.get("BASE16_COLOR_00_HEX")}") + print(f"#{os.environ.get("BASE16_COLOR_08_HEX")}") + elif load > 60: + print(f"#{os.environ.get("BASE16_COLOR_00_HEX")}") + print(f"#{os.environ.get("BASE16_COLOR_0A_HEX")}") + elif load > 30: + print(f"#{os.environ.get("BASE16_COLOR_00_HEX")}") + print(f"#{os.environ.get("BASE16_COLOR_0C_HEX")}") + + +if __name__ == "__main__": + main() diff --git a/i3blocks/.local/bin/i3blocks-memory b/i3blocks/.local/bin/i3blocks-memory new file mode 100755 index 0000000..f62d5c7 --- /dev/null +++ b/i3blocks/.local/bin/i3blocks-memory @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 + +import os +import linecache + +ICON = "\U000f035b" # 󰍛 + + +def main(): + linecache.getline("/proc/meminfo", 0) + linecache.getline("/proc/meminfo", 2) + + with open("/proc/meminfo", "r") as f: + meminfo = dict( + (i.split()[0].rstrip(":"), int(i.split()[1])) for i in f.readlines() + ) + + used = 100 - round(100 * meminfo["MemAvailable"] / meminfo["MemTotal"]) + + print(f" {ICON} {used}% \n") + if used > 90: + print(f"#{os.environ.get("BASE16_COLOR_00_HEX")}") + print(f"#{os.environ.get("BASE16_COLOR_08_HEX")}") + elif used > 75: + print(f"#{os.environ.get("BASE16_COLOR_00_HEX")}") + print(f"#{os.environ.get("BASE16_COLOR_0A_HEX")}") + + +if __name__ == "__main__": + main() diff --git a/i3blocks/.local/bin/i3blocks-miniflux b/i3blocks/.local/bin/i3blocks-miniflux new file mode 100755 index 0000000..f87f1d3 --- /dev/null +++ b/i3blocks/.local/bin/i3blocks-miniflux @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 + +import os +import subprocess + +import requests + +ICON = "\U000f046b" # 󰑫 + + +def get_count(): + url = "https://miniflux.tobyvin.dev/v1/feeds/counters" + headers = {"X-Auth-Token": os.environ.get("MINIFLUX_TOKEN")} + resp = requests.get(url, headers=headers) + return sum(resp.json().get("unreads").values()) + + +def main(): + match int(os.environ.get("BLOCK_BUTTON", "0")): + case 1: + subprocess.run(["xdg-open", "https://miniflux.tobyvin.dev"]) + + try: + count = get_count() + except requests.exceptions.ConnectionError: + count = "x" + + print(f" {ICON} {count} \n") + match count: + case str(c) | int(c) if c > 10: + print(f"#{os.environ.get("BASE16_COLOR_00_HEX")}") + print(f"#{os.environ.get("BASE16_COLOR_08_HEX")}") + case int(c) if c > 0: + print(f"#{os.environ.get("BASE16_COLOR_00_HEX")}") + print(f"#{os.environ.get("BASE16_COLOR_0A_HEX")}") + + +if __name__ == "__main__": + main() diff --git a/i3blocks/.local/bin/i3blocks-net b/i3blocks/.local/bin/i3blocks-net new file mode 100755 index 0000000..2faa071 --- /dev/null +++ b/i3blocks/.local/bin/i3blocks-net @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 + +import glob +import os +from enum import Enum + + +class IFaceType(Enum): + ETHERNET = "\U000f0200" + WIRELESS = "\U000f05a9" + TUNNEL = "\U000f0582" + UNKNOWN = "" + + @staticmethod + def from_path(path: str): + try: + with open(os.path.join(path, "type"), "r") as f: + match int(f.readline()): + case 1 if os.path.isdir( + os.path.join(path, "wireless") + ) or os.path.islink(os.path.join(path, "phy80211")): + return IFaceType.WIRELESS + case 1: + return IFaceType.ETHERNET + case 65534: + return IFaceType.TUNNEL + case _: + raise ValueError("Invalid interface type") + except Exception: + return IFaceType.UNKNOWN + + +class IFace: + def __init__(self, path: str): + self.path = path + self.name = os.path.basename(path) + self._type = IFaceType.from_path(path) + + def __str__(self): + return self._type.value + + +def main(): + ifaces = [] + for net in glob.glob("/sys/class/net/*"): + iface = IFace(net) + if iface.name != "lo" and iface._type != IFaceType.UNKNOWN: + ifaces.append(str(iface)) + + print(f" {" ".join(ifaces)} \n") + + +if __name__ == "__main__": + main() diff --git a/i3blocks/.local/bin/i3blocks-notmuch b/i3blocks/.local/bin/i3blocks-notmuch new file mode 100755 index 0000000..a81fae6 --- /dev/null +++ b/i3blocks/.local/bin/i3blocks-notmuch @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 + +import notmuch +import os + +ICON = "\U000f01ee" # 󰇮 + + +def main(): + # Workaround for bug in the notmuch module's default config resolution + if os.environ.get("NOTMUCH_CONFIG") is None: + os.environ["NOTMUCH_CONFIG"] = os.path.join( + os.environ.get( + "XDG_CONFIG_HOME", os.path.join(os.path.expanduser("~"), ".config") + ), + "notmuch", + os.environ.get("NOTMUCH_PROFILE", "default"), + "config", + ) + + count = notmuch.Database().create_query("tag:unread").count_messages() + + print(f" {ICON} {count} \n") + if count > 10: + print(f"#{os.environ.get("BASE16_COLOR_00_HEX")}") + print(f"#{os.environ.get("BASE16_COLOR_08_HEX")}") + elif count > 0: + print(f"#{os.environ.get("BASE16_COLOR_00_HEX")}") + print(f"#{os.environ.get("BASE16_COLOR_0A_HEX")}") + + +if __name__ == "__main__": + main() diff --git a/i3blocks/.local/bin/i3blocks-title b/i3blocks/.local/bin/i3blocks-title new file mode 100755 index 0000000..c014adc --- /dev/null +++ b/i3blocks/.local/bin/i3blocks-title @@ -0,0 +1,17 @@ +#!/bin/sh + +into_block='{ + "full_text": (.name[0:50] // ""), + "short_text": ([.app_id, .window_title, .name] | map(select(. != null and . != "")) | (first // "") | .[0:25]), +}' + +swaymsg -t get_tree | jq -c '.. | select(.focused? == true and .type == "con") | '"$into_block" + +swaymsg --monitor -t subscribe '["window", "workspace"]' | jq -c --unbuffered ' + select((.change | test("close|focus|title")) and (.current.type == "workspace" or .container.focused)) | + if .current.type == "workspace" or .change == "close" then + { "full_text": "" } + else + .container | '"$into_block"' + end +' diff --git a/i3blocks/.local/bin/i3blocks-volume b/i3blocks/.local/bin/i3blocks-volume new file mode 100755 index 0000000..40854e7 --- /dev/null +++ b/i3blocks/.local/bin/i3blocks-volume @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 + +import asyncio +import json +import os +import subprocess +import sys + + +ICONS = { + "mute": "\U000f075f", # 󰝟 + "low": "\U000f057f", # 󰕿 + "medium": "\U000f0580", # 󰖀 + "high": "\U000f057e", # 󰕾 +} + + +def is_muted(): + return ( + subprocess.run( + ["pactl", "get-sink-mute", "@DEFAULT_SINK@"], + capture_output=True, + encoding="UTF-8", + ) + .stdout.removeprefix("Mute: ") + .strip() + == "yes" + ) + + +def get_volume(): + stdout = subprocess.run( + ["pactl", "get-sink-volume", "@DEFAULT_SINK@"], + capture_output=True, + encoding="UTF-8", + ).stdout.strip() + + for s in stdout.removeprefix("Volume: ").split(): + if s.endswith("%"): + return int(s.rstrip("%")) + + +def print_status(): + match get_volume(): + case None: + output = {} + case v if is_muted(): + output = { + "full_text": f" {ICONS["mute"]} {v}% ", + "color": f"#{os.environ.get("BASE16_COLOR_00_HEX")}", + "background": f"#{os.environ.get("BASE16_COLOR_0A_HEX")}", + } + case v if v > 66: + output = {"full_text": f" {ICONS["high"]} {v}% "} + case v if v > 33: + output = {"full_text": f" {ICONS["medium"]} {v}% "} + case v: + output = {"full_text": f" {ICONS["low"]} {v}% "} + + print(json.dumps(output, ensure_ascii=False), flush=True) + + +async def listener(): + process = await asyncio.create_subprocess_exec( + "pactl", + "--format=json", + "subscribe", + stdout=asyncio.subprocess.PIPE, + ) + + while True: + line = await process.stdout.readline() + + if not line: + await asyncio.sleep(1) + continue + + match json.loads(line.decode("UTF-8")): + case {"on": "sink"} | {"on": "source-output"}: + print_status() + + +async def button_handler(): + loop = asyncio.get_event_loop() + reader = asyncio.StreamReader() + protocol = asyncio.StreamReaderProtocol(reader) + await loop.connect_read_pipe(lambda: protocol, sys.stdin) + proc: subprocess.Popen = None + + while True: + line = await reader.readline() + + if not line: + await asyncio.sleep(1) + continue + + match json.loads(line): + case {"button": 1} if proc: + proc.terminate() + proc = None + case {"button": 1, "gui": cmd}: + proc = subprocess.Popen(cmd, shell=True) + case {"button": 2}: + pass + case {"button": 3}: + subprocess.run(["wpctl", "set-mute", "@DEFAULT_AUDIO_SINK@", "toggle"]) + case {"button": 4}: + subprocess.run( + ["wpctl", "set-volume", "-l", "1.5", "@DEFAULT_AUDIO_SINK@", "5%+"] + ) + case {"button": 5}: + subprocess.run( + ["wpctl", "set-volume", "-l", "1.5", "@DEFAULT_AUDIO_SINK@", "5%-"] + ) + + +async def main(): + print_status() + try: + async with asyncio.TaskGroup() as task_group: + task_group.create_task(listener()) + task_group.create_task(button_handler()) + except asyncio.CancelledError: + return + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/i3blocks/.local/bin/i3blocks-wttr b/i3blocks/.local/bin/i3blocks-wttr new file mode 100755 index 0000000..8ae4857 --- /dev/null +++ b/i3blocks/.local/bin/i3blocks-wttr @@ -0,0 +1,152 @@ +#!/usr/bin/env python3 + +import time + +import requests + +ICON = "\U000f046b" # 󰑫 + +WWO_CODE = { + "113": "Sunny", + "116": "PartlyCloudy", + "119": "Cloudy", + "122": "VeryCloudy", + "143": "Fog", + "176": "LightShowers", + "179": "LightSleetShowers", + "182": "LightSleet", + "185": "LightSleet", + "200": "ThunderyShowers", + "227": "LightSnow", + "230": "HeavySnow", + "248": "Fog", + "260": "Fog", + "263": "LightShowers", + "266": "LightRain", + "281": "LightSleet", + "284": "LightSleet", + "293": "LightRain", + "296": "LightRain", + "299": "HeavyShowers", + "302": "HeavyRain", + "305": "HeavyShowers", + "308": "HeavyRain", + "311": "LightSleet", + "314": "LightSleet", + "317": "LightSleet", + "320": "LightSnow", + "323": "LightSnowShowers", + "326": "LightSnowShowers", + "329": "HeavySnow", + "332": "HeavySnow", + "335": "HeavySnowShowers", + "338": "HeavySnow", + "350": "LightSleet", + "353": "LightShowers", + "356": "HeavyShowers", + "359": "HeavyRain", + "362": "LightSleetShowers", + "365": "LightSleetShowers", + "368": "LightSnowShowers", + "371": "HeavySnowShowers", + "374": "LightSleetShowers", + "377": "LightSleet", + "386": "ThunderyShowers", + "389": "ThunderyHeavyRain", + "392": "ThunderySnowShowers", + "395": "HeavySnowShowers", +} + +WEATHER_SYMBOL_WI_DAY = { + "Unknown": "", + "Cloudy": "", + "Fog": "", + "HeavyRain": "", + "HeavyShowers": "", + "HeavySnow": "", + "HeavySnowShowers": "", + "LightRain": "", + "LightShowers": "", + "LightSleet": "", + "LightSleetShowers": "", + "LightSnow": "", + "LightSnowShowers": "", + "PartlyCloudy": "", + "Sunny": "", + "ThunderyHeavyRain": "", + "ThunderyShowers": "", + "ThunderySnowShowers": "", + "VeryCloudy": "", +} + +WEATHER_SYMBOL_WI_NIGHT = { + "Unknown": "", + "Cloudy": "", + "Fog": "", + "HeavyRain": "", + "HeavyShowers": "", + "HeavySnow": "", + "HeavySnowShowers": "", + "LightRain": "", + "LightShowers": "", + "LightSleet": "", + "LightSleetShowers": "", + "LightSnow": "", + "LightSnowShowers": "", + "PartlyCloudy": "", + "Sunny": "", + "ThunderyHeavyRain": "", + "ThunderyShowers": "", + "ThunderySnowShowers": "", + "VeryCloudy": "", +} + + +def parse_data() -> str: + data = requests.get("https://wttr.in/?format=j1").json() + + current = next(iter(data.get("current_condition", [])), None) + + if current is None: + return "" + + code = current.get("weatherCode", "0") + key = WWO_CODE.get(code, "Unknown") + + local_obs_date, local_obs_time = current.get("localObsDateTime").split(maxsplit=1) + astronomy = next( + iter( + next( + w.get("astronomy") + for w in data.get("weather", []) + if w.get("date", None) == local_obs_date and w.get("astronomy", None) + ) + ), + None, + ) + + if astronomy is None: + return None + + local = time.strptime(local_obs_time, "%I:%M %p") + sunrise = time.strptime(astronomy.get("sunrise"), "%I:%M %p") + sunset = time.strptime(astronomy.get("sunset"), "%I:%M %p") + + if local > sunrise and local < sunset: + icon = WEATHER_SYMBOL_WI_DAY.get(key) + else: + icon = WEATHER_SYMBOL_WI_NIGHT.get(key) + + if temp := current.get("temp_C"): + return " {} {}°C ".format(icon, temp) + + +def main(): + try: + print(parse_data()) + except Exception: + print() + + +if __name__ == "__main__": + main() diff --git a/i3blocks/.local/lib/i3blocks/i3blocks-cpu b/i3blocks/.local/lib/i3blocks/i3blocks-cpu deleted file mode 100755 index ddb1d68..0000000 --- a/i3blocks/.local/lib/i3blocks/i3blocks-cpu +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env python3 - -import os -import json - -ICON = "\U000f0ee0" # 󰻠 -BLACK = f"#{os.environ.get("BASE16_COLOR_00_HEX")}" -RED = f"#{os.environ.get("BASE16_COLOR_08_HEX")}" -YELLOW = f"#{os.environ.get("BASE16_COLOR_0A_HEX")}" -AQUA = f"#{os.environ.get("BASE16_COLOR_0C_HEX")}" - - -def main(): - off = json.loads(os.environ.get("values", json.dumps([0 for i in range(10)]))) - - with open("/proc/stat", "r") as f: - val = [int(x) for x in f.readline().strip().split(" ")[2:]] - - if elapsed := sum(val + off): - usage = sum(val[0:3] + val[5:] + off[0:3] + off[5:]) / elapsed - else: - usage = 0 - - output = { - "full_text": " {} {:.0%} ".format(ICON, usage), - "values": [-x for x in val], - } - if usage > 0.9: - output["color"] = BLACK - output["background"] = RED - elif usage > 0.6: - output["color"] = BLACK - output["background"] = YELLOW - elif usage > 0.3: - output["color"] = BLACK - output["background"] = AQUA - - print(json.dumps(output, ensure_ascii=False), flush=True) - - -if __name__ == "__main__": - main() diff --git a/i3blocks/.local/lib/i3blocks/i3blocks-disk b/i3blocks/.local/lib/i3blocks/i3blocks-disk deleted file mode 100755 index a98a40c..0000000 --- a/i3blocks/.local/lib/i3blocks/i3blocks-disk +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python3 - -import os - -ICON = "\U000f02ca" # 󰋊 -GB = 1073741824 - - -def main(): - statvfs = os.statvfs("/") - avail = bytes = statvfs.f_bavail * statvfs.f_frsize - - for unit in ["B", "KB", "MB", "GB", "TB", "PB"]: - if avail < 1024.0 or unit == "PB": - break - avail /= 1024.0 - - print(f" {ICON} {round(avail)}{unit} \n") - if bytes < (20 * GB): - print(f"#{os.environ.get("BASE16_COLOR_00_HEX")}") - print(f"#{os.environ.get("BASE16_COLOR_08_HEX")}") - elif bytes < (10 * GB): - print(f"#{os.environ.get("BASE16_COLOR_00_HEX")}") - print(f"#{os.environ.get("BASE16_COLOR_0A_HEX")}") - - -if __name__ == "__main__": - main() diff --git a/i3blocks/.local/lib/i3blocks/i3blocks-dunst b/i3blocks/.local/lib/i3blocks/i3blocks-dunst deleted file mode 100755 index df3d9d4..0000000 --- a/i3blocks/.local/lib/i3blocks/i3blocks-dunst +++ /dev/null @@ -1,146 +0,0 @@ -#!/usr/bin/env python3 - -import asyncio -import os -import sys -import json -import pprint - -from dbus_next.aio import MessageBus -import i3ipc -from i3ipc.aio import Connection - - -BUS_NAME = "org.freedesktop.Notifications" -OBJECT_PATH = "/org/freedesktop/Notifications" -DUNST_INTERFACE = "org.dunstproject.cmd0" -PROPERTIES_INTERFACE = "org.freedesktop.DBus.Properties" -I3_EVENTS = [ - i3ipc.Event.WINDOW_FOCUS, - i3ipc.Event.WINDOW_FULLSCREEN_MODE, - i3ipc.Event.WINDOW_CLOSE, - i3ipc.Event.WORKSPACE_FOCUS, -] - - -class Dunst: - @classmethod - async def connect(cls): - self = cls() - self.bus = await MessageBus().connect() - - self.obj = self.bus.get_proxy_object( - BUS_NAME, - OBJECT_PATH, - await self.bus.introspect(BUS_NAME, OBJECT_PATH), - ) - - self.dunst = self.obj.get_interface(DUNST_INTERFACE) - self.paused = await self.dunst.get_paused() - self.displayed = await self.dunst.get_displayed_length() - self.waiting = await self.dunst.get_waiting_length() - - self.i3 = await Connection().connect() - - for event in I3_EVENTS: - self.i3.on(event, self.handle_i3_event) - - return self - - async def handle_i3_event(self, conn, e): - if e.change == "close": - enable = 0 - elif isinstance(e, i3ipc.WorkspaceEvent): - enable = e.current.fullscreen_mode - else: - enable = e.container.fullscreen_mode - - await self.dunst.call_rule_enable("transient_skip", enable) - - - def print_status(self): - if self.paused: - icon = "\U000f009b" # 󰂛 - else: - icon = "\U000f009a" # 󰂚 - - if self.waiting > 0: - count = f" {self.waiting}" - else: - count = "" - - output = {"full_text": f" {icon}{count} "} - - if self.displayed + self.waiting > 0: - output["color"] = f"#{os.environ.get("BASE16_COLOR_00_HEX")}" - output["background"] = f"#{os.environ.get("BASE16_COLOR_0C_HEX")}" - elif self.paused: - output["color"] = f"#{os.environ.get("BASE16_COLOR_00_HEX")}" - output["background"] = f"#{os.environ.get("BASE16_COLOR_0A_HEX")}" - - print(json.dumps(output, ensure_ascii=False), flush=True) - - def on_properties_changed(self, _name, property, invalidated): - update = False - for property, variant in property.items(): - match property: - case "paused": - self.paused = variant.value - case "displayedLength": - self.displayed = variant.value - case "waitingLength": - self.waiting = variant.value - case _: - continue - update = True - - if update: - self.print_status() - - async def listener(self): - properties = self.obj.get_interface(PROPERTIES_INTERFACE) - properties.on_properties_changed(self.on_properties_changed) - - await self.bus.wait_for_disconnect() - - async def button_handler(self): - loop = asyncio.get_event_loop() - reader = asyncio.StreamReader() - protocol = asyncio.StreamReaderProtocol(reader) - await loop.connect_read_pipe(lambda: protocol, sys.stdin) - - while True: - line = await reader.readline() - - if not line: - await asyncio.sleep(1) - continue - - match json.loads(line): - case {"button": 1}: - await self.dunst.call_notification_show() - case {"button": 2}: - pass - case {"button": 3}: - await self.dunst.set_paused(not self.paused) - case {"button": 4}: - pass - case {"button": 5}: - pass - - -async def main(): - dunst = await Dunst.connect() - dunst.print_status() - - try: - async with asyncio.TaskGroup() as task_group: - task_group.create_task(dunst.i3.main()) - task_group.create_task(dunst.listener()) - task_group.create_task(dunst.button_handler()) - except asyncio.CancelledError: - return - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/i3blocks/.local/lib/i3blocks/i3blocks-gpu b/i3blocks/.local/lib/i3blocks/i3blocks-gpu deleted file mode 100755 index df96d6c..0000000 --- a/i3blocks/.local/lib/i3blocks/i3blocks-gpu +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env python3 - -import os -import glob - -ICON = "\U000f0379" # 󰍹 - - -def main(): - files = glob.glob("/sys/class/drm/card*/device/gpu_busy_percent") - if not files: - return - - with open(files[0]) as f: - load = int(f.readline()) - - print(f" {ICON} {load}% \n") - if load > 90: - print(f"#{os.environ.get("BASE16_COLOR_00_HEX")}") - print(f"#{os.environ.get("BASE16_COLOR_08_HEX")}") - elif load > 60: - print(f"#{os.environ.get("BASE16_COLOR_00_HEX")}") - print(f"#{os.environ.get("BASE16_COLOR_0A_HEX")}") - elif load > 30: - print(f"#{os.environ.get("BASE16_COLOR_00_HEX")}") - print(f"#{os.environ.get("BASE16_COLOR_0C_HEX")}") - - -if __name__ == "__main__": - main() diff --git a/i3blocks/.local/lib/i3blocks/i3blocks-memory b/i3blocks/.local/lib/i3blocks/i3blocks-memory deleted file mode 100755 index f62d5c7..0000000 --- a/i3blocks/.local/lib/i3blocks/i3blocks-memory +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env python3 - -import os -import linecache - -ICON = "\U000f035b" # 󰍛 - - -def main(): - linecache.getline("/proc/meminfo", 0) - linecache.getline("/proc/meminfo", 2) - - with open("/proc/meminfo", "r") as f: - meminfo = dict( - (i.split()[0].rstrip(":"), int(i.split()[1])) for i in f.readlines() - ) - - used = 100 - round(100 * meminfo["MemAvailable"] / meminfo["MemTotal"]) - - print(f" {ICON} {used}% \n") - if used > 90: - print(f"#{os.environ.get("BASE16_COLOR_00_HEX")}") - print(f"#{os.environ.get("BASE16_COLOR_08_HEX")}") - elif used > 75: - print(f"#{os.environ.get("BASE16_COLOR_00_HEX")}") - print(f"#{os.environ.get("BASE16_COLOR_0A_HEX")}") - - -if __name__ == "__main__": - main() diff --git a/i3blocks/.local/lib/i3blocks/i3blocks-miniflux b/i3blocks/.local/lib/i3blocks/i3blocks-miniflux deleted file mode 100755 index f87f1d3..0000000 --- a/i3blocks/.local/lib/i3blocks/i3blocks-miniflux +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python3 - -import os -import subprocess - -import requests - -ICON = "\U000f046b" # 󰑫 - - -def get_count(): - url = "https://miniflux.tobyvin.dev/v1/feeds/counters" - headers = {"X-Auth-Token": os.environ.get("MINIFLUX_TOKEN")} - resp = requests.get(url, headers=headers) - return sum(resp.json().get("unreads").values()) - - -def main(): - match int(os.environ.get("BLOCK_BUTTON", "0")): - case 1: - subprocess.run(["xdg-open", "https://miniflux.tobyvin.dev"]) - - try: - count = get_count() - except requests.exceptions.ConnectionError: - count = "x" - - print(f" {ICON} {count} \n") - match count: - case str(c) | int(c) if c > 10: - print(f"#{os.environ.get("BASE16_COLOR_00_HEX")}") - print(f"#{os.environ.get("BASE16_COLOR_08_HEX")}") - case int(c) if c > 0: - print(f"#{os.environ.get("BASE16_COLOR_00_HEX")}") - print(f"#{os.environ.get("BASE16_COLOR_0A_HEX")}") - - -if __name__ == "__main__": - main() diff --git a/i3blocks/.local/lib/i3blocks/i3blocks-net b/i3blocks/.local/lib/i3blocks/i3blocks-net deleted file mode 100755 index 2faa071..0000000 --- a/i3blocks/.local/lib/i3blocks/i3blocks-net +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env python3 - -import glob -import os -from enum import Enum - - -class IFaceType(Enum): - ETHERNET = "\U000f0200" - WIRELESS = "\U000f05a9" - TUNNEL = "\U000f0582" - UNKNOWN = "" - - @staticmethod - def from_path(path: str): - try: - with open(os.path.join(path, "type"), "r") as f: - match int(f.readline()): - case 1 if os.path.isdir( - os.path.join(path, "wireless") - ) or os.path.islink(os.path.join(path, "phy80211")): - return IFaceType.WIRELESS - case 1: - return IFaceType.ETHERNET - case 65534: - return IFaceType.TUNNEL - case _: - raise ValueError("Invalid interface type") - except Exception: - return IFaceType.UNKNOWN - - -class IFace: - def __init__(self, path: str): - self.path = path - self.name = os.path.basename(path) - self._type = IFaceType.from_path(path) - - def __str__(self): - return self._type.value - - -def main(): - ifaces = [] - for net in glob.glob("/sys/class/net/*"): - iface = IFace(net) - if iface.name != "lo" and iface._type != IFaceType.UNKNOWN: - ifaces.append(str(iface)) - - print(f" {" ".join(ifaces)} \n") - - -if __name__ == "__main__": - main() diff --git a/i3blocks/.local/lib/i3blocks/i3blocks-notmuch b/i3blocks/.local/lib/i3blocks/i3blocks-notmuch deleted file mode 100755 index a81fae6..0000000 --- a/i3blocks/.local/lib/i3blocks/i3blocks-notmuch +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python3 - -import notmuch -import os - -ICON = "\U000f01ee" # 󰇮 - - -def main(): - # Workaround for bug in the notmuch module's default config resolution - if os.environ.get("NOTMUCH_CONFIG") is None: - os.environ["NOTMUCH_CONFIG"] = os.path.join( - os.environ.get( - "XDG_CONFIG_HOME", os.path.join(os.path.expanduser("~"), ".config") - ), - "notmuch", - os.environ.get("NOTMUCH_PROFILE", "default"), - "config", - ) - - count = notmuch.Database().create_query("tag:unread").count_messages() - - print(f" {ICON} {count} \n") - if count > 10: - print(f"#{os.environ.get("BASE16_COLOR_00_HEX")}") - print(f"#{os.environ.get("BASE16_COLOR_08_HEX")}") - elif count > 0: - print(f"#{os.environ.get("BASE16_COLOR_00_HEX")}") - print(f"#{os.environ.get("BASE16_COLOR_0A_HEX")}") - - -if __name__ == "__main__": - main() diff --git a/i3blocks/.local/lib/i3blocks/i3blocks-title b/i3blocks/.local/lib/i3blocks/i3blocks-title deleted file mode 100755 index c014adc..0000000 --- a/i3blocks/.local/lib/i3blocks/i3blocks-title +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh - -into_block='{ - "full_text": (.name[0:50] // ""), - "short_text": ([.app_id, .window_title, .name] | map(select(. != null and . != "")) | (first // "") | .[0:25]), -}' - -swaymsg -t get_tree | jq -c '.. | select(.focused? == true and .type == "con") | '"$into_block" - -swaymsg --monitor -t subscribe '["window", "workspace"]' | jq -c --unbuffered ' - select((.change | test("close|focus|title")) and (.current.type == "workspace" or .container.focused)) | - if .current.type == "workspace" or .change == "close" then - { "full_text": "" } - else - .container | '"$into_block"' - end -' diff --git a/i3blocks/.local/lib/i3blocks/i3blocks-volume b/i3blocks/.local/lib/i3blocks/i3blocks-volume deleted file mode 100755 index 40854e7..0000000 --- a/i3blocks/.local/lib/i3blocks/i3blocks-volume +++ /dev/null @@ -1,128 +0,0 @@ -#!/usr/bin/env python3 - -import asyncio -import json -import os -import subprocess -import sys - - -ICONS = { - "mute": "\U000f075f", # 󰝟 - "low": "\U000f057f", # 󰕿 - "medium": "\U000f0580", # 󰖀 - "high": "\U000f057e", # 󰕾 -} - - -def is_muted(): - return ( - subprocess.run( - ["pactl", "get-sink-mute", "@DEFAULT_SINK@"], - capture_output=True, - encoding="UTF-8", - ) - .stdout.removeprefix("Mute: ") - .strip() - == "yes" - ) - - -def get_volume(): - stdout = subprocess.run( - ["pactl", "get-sink-volume", "@DEFAULT_SINK@"], - capture_output=True, - encoding="UTF-8", - ).stdout.strip() - - for s in stdout.removeprefix("Volume: ").split(): - if s.endswith("%"): - return int(s.rstrip("%")) - - -def print_status(): - match get_volume(): - case None: - output = {} - case v if is_muted(): - output = { - "full_text": f" {ICONS["mute"]} {v}% ", - "color": f"#{os.environ.get("BASE16_COLOR_00_HEX")}", - "background": f"#{os.environ.get("BASE16_COLOR_0A_HEX")}", - } - case v if v > 66: - output = {"full_text": f" {ICONS["high"]} {v}% "} - case v if v > 33: - output = {"full_text": f" {ICONS["medium"]} {v}% "} - case v: - output = {"full_text": f" {ICONS["low"]} {v}% "} - - print(json.dumps(output, ensure_ascii=False), flush=True) - - -async def listener(): - process = await asyncio.create_subprocess_exec( - "pactl", - "--format=json", - "subscribe", - stdout=asyncio.subprocess.PIPE, - ) - - while True: - line = await process.stdout.readline() - - if not line: - await asyncio.sleep(1) - continue - - match json.loads(line.decode("UTF-8")): - case {"on": "sink"} | {"on": "source-output"}: - print_status() - - -async def button_handler(): - loop = asyncio.get_event_loop() - reader = asyncio.StreamReader() - protocol = asyncio.StreamReaderProtocol(reader) - await loop.connect_read_pipe(lambda: protocol, sys.stdin) - proc: subprocess.Popen = None - - while True: - line = await reader.readline() - - if not line: - await asyncio.sleep(1) - continue - - match json.loads(line): - case {"button": 1} if proc: - proc.terminate() - proc = None - case {"button": 1, "gui": cmd}: - proc = subprocess.Popen(cmd, shell=True) - case {"button": 2}: - pass - case {"button": 3}: - subprocess.run(["wpctl", "set-mute", "@DEFAULT_AUDIO_SINK@", "toggle"]) - case {"button": 4}: - subprocess.run( - ["wpctl", "set-volume", "-l", "1.5", "@DEFAULT_AUDIO_SINK@", "5%+"] - ) - case {"button": 5}: - subprocess.run( - ["wpctl", "set-volume", "-l", "1.5", "@DEFAULT_AUDIO_SINK@", "5%-"] - ) - - -async def main(): - print_status() - try: - async with asyncio.TaskGroup() as task_group: - task_group.create_task(listener()) - task_group.create_task(button_handler()) - except asyncio.CancelledError: - return - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/i3blocks/.local/lib/i3blocks/i3blocks-wttr b/i3blocks/.local/lib/i3blocks/i3blocks-wttr deleted file mode 100755 index 8ae4857..0000000 --- a/i3blocks/.local/lib/i3blocks/i3blocks-wttr +++ /dev/null @@ -1,152 +0,0 @@ -#!/usr/bin/env python3 - -import time - -import requests - -ICON = "\U000f046b" # 󰑫 - -WWO_CODE = { - "113": "Sunny", - "116": "PartlyCloudy", - "119": "Cloudy", - "122": "VeryCloudy", - "143": "Fog", - "176": "LightShowers", - "179": "LightSleetShowers", - "182": "LightSleet", - "185": "LightSleet", - "200": "ThunderyShowers", - "227": "LightSnow", - "230": "HeavySnow", - "248": "Fog", - "260": "Fog", - "263": "LightShowers", - "266": "LightRain", - "281": "LightSleet", - "284": "LightSleet", - "293": "LightRain", - "296": "LightRain", - "299": "HeavyShowers", - "302": "HeavyRain", - "305": "HeavyShowers", - "308": "HeavyRain", - "311": "LightSleet", - "314": "LightSleet", - "317": "LightSleet", - "320": "LightSnow", - "323": "LightSnowShowers", - "326": "LightSnowShowers", - "329": "HeavySnow", - "332": "HeavySnow", - "335": "HeavySnowShowers", - "338": "HeavySnow", - "350": "LightSleet", - "353": "LightShowers", - "356": "HeavyShowers", - "359": "HeavyRain", - "362": "LightSleetShowers", - "365": "LightSleetShowers", - "368": "LightSnowShowers", - "371": "HeavySnowShowers", - "374": "LightSleetShowers", - "377": "LightSleet", - "386": "ThunderyShowers", - "389": "ThunderyHeavyRain", - "392": "ThunderySnowShowers", - "395": "HeavySnowShowers", -} - -WEATHER_SYMBOL_WI_DAY = { - "Unknown": "", - "Cloudy": "", - "Fog": "", - "HeavyRain": "", - "HeavyShowers": "", - "HeavySnow": "", - "HeavySnowShowers": "", - "LightRain": "", - "LightShowers": "", - "LightSleet": "", - "LightSleetShowers": "", - "LightSnow": "", - "LightSnowShowers": "", - "PartlyCloudy": "", - "Sunny": "", - "ThunderyHeavyRain": "", - "ThunderyShowers": "", - "ThunderySnowShowers": "", - "VeryCloudy": "", -} - -WEATHER_SYMBOL_WI_NIGHT = { - "Unknown": "", - "Cloudy": "", - "Fog": "", - "HeavyRain": "", - "HeavyShowers": "", - "HeavySnow": "", - "HeavySnowShowers": "", - "LightRain": "", - "LightShowers": "", - "LightSleet": "", - "LightSleetShowers": "", - "LightSnow": "", - "LightSnowShowers": "", - "PartlyCloudy": "", - "Sunny": "", - "ThunderyHeavyRain": "", - "ThunderyShowers": "", - "ThunderySnowShowers": "", - "VeryCloudy": "", -} - - -def parse_data() -> str: - data = requests.get("https://wttr.in/?format=j1").json() - - current = next(iter(data.get("current_condition", [])), None) - - if current is None: - return "" - - code = current.get("weatherCode", "0") - key = WWO_CODE.get(code, "Unknown") - - local_obs_date, local_obs_time = current.get("localObsDateTime").split(maxsplit=1) - astronomy = next( - iter( - next( - w.get("astronomy") - for w in data.get("weather", []) - if w.get("date", None) == local_obs_date and w.get("astronomy", None) - ) - ), - None, - ) - - if astronomy is None: - return None - - local = time.strptime(local_obs_time, "%I:%M %p") - sunrise = time.strptime(astronomy.get("sunrise"), "%I:%M %p") - sunset = time.strptime(astronomy.get("sunset"), "%I:%M %p") - - if local > sunrise and local < sunset: - icon = WEATHER_SYMBOL_WI_DAY.get(key) - else: - icon = WEATHER_SYMBOL_WI_NIGHT.get(key) - - if temp := current.get("temp_C"): - return " {} {}°C ".format(icon, temp) - - -def main(): - try: - print(parse_data()) - except Exception: - print() - - -if __name__ == "__main__": - main() -- cgit v1.2.3-70-g09d2