Dotfiles/desktopenvs/hyprlua/astal-menu/modules/weather.py

73 lines
2.4 KiB
Python

"""Weather quad: wttr.in rendered with its original ANSI/CLI art (via AnsiRenderer).
Reuses the shared LocationService for the city; if none is known yet, wttr.in
geolocates the caller's IP itself, so the widget still works standalone. The
'ascii_art' feature toggle swaps the art for a compact one-line text summary.
"""
from __future__ import annotations
import gi
gi.require_version("Gtk", "4.0")
from gi.repository import Gtk # noqa: E402
from lib.ansi import AnsiRenderer
from lib.proc import run_text
from module_base import Feature, ModuleContext, ModuleInstance, ModuleSpec
from paths import BACKEND_DIR
_SCRIPT = str(BACKEND_DIR / "weather.sh")
class _WeatherView(Gtk.Box):
def __init__(self, ctx: ModuleContext, opts: str):
super().__init__(orientation=Gtk.Orientation.VERTICAL)
self.add_css_class("weather-view")
self.ctx = ctx
self.opts = opts
self._loc = ""
self.renderer = AnsiRenderer()
self.append(self.renderer.view)
self._status = Gtk.Label(label="Loading weather…")
self._status.add_css_class("weather-status")
self.append(self._status)
ctx.services.location.subscribe(self._on_location)
self.refresh()
def _on_location(self, data: dict) -> None:
self._loc = data.get("city") or ""
self.refresh()
def refresh(self) -> None:
art = self.ctx.feature("ascii_art", True)
opts = self.opts if art else "format=%l:+%c+%t,+%w"
run_text([_SCRIPT, self._loc, opts], self._on_result)
def _on_result(self, ok: bool, out: str, err: str) -> None:
if ok and out.strip() and "<html" not in out.lower():
self.renderer.set_text(out.rstrip("\n"))
self._status.set_visible(False)
else:
self._status.set_text("Weather unavailable")
self._status.set_visible(True)
def build(ctx: ModuleContext) -> ModuleInstance:
# The ascii_art toggle rebuilds the whole card via QuadCard, so both views are
# recreated and re-fetch with the current format — no partial refresh here.
compact = _WeatherView(ctx, opts="0") # current conditions only
expanded = _WeatherView(ctx, opts="") # full 3-day forecast
return ModuleInstance(compact=compact, expanded=expanded)
SPEC = ModuleSpec(
id="weather",
title="Weather",
icon="", # nf-weather
build=build,
features=[Feature("ascii_art", "CLI art", True)],
)