Dotfiles/desktopenvs/hyprdrive/astro-menu/services/location.py

56 lines
1.5 KiB
Python

"""Shared geolocation singleton.
Runs backend/geolocate.py (IP geolocation, cached) exactly once and notifies
subscribers. Both the Location map and the Weather widget subscribe, so there is a
single source of truth for "where am I" and only one network lookup.
"""
from __future__ import annotations
import sys
from typing import Callable, Optional
from lib.proc import run_json
from paths import BACKEND_DIR
class LocationService:
def __init__(self) -> None:
self.data: Optional[dict] = None
self._subs: list[Callable[[dict], None]] = []
self._inflight = False
def subscribe(self, cb: Callable[[dict], None]) -> None:
self._subs.append(cb)
if self.data is not None:
cb(self.data)
def get(self) -> Optional[dict]:
if self.data is None and not self._inflight:
self.refresh()
return self.data
def refresh(self) -> None:
if self._inflight:
return
self._inflight = True
argv = [sys.executable, str(BACKEND_DIR / "geolocate.py")]
run_json(argv, self._on_result)
def _on_result(self, ok: bool, data) -> None:
self._inflight = False
if ok and isinstance(data, dict) and "lat" in data:
self.data = data
for cb in list(self._subs):
cb(data)
_instance: LocationService | None = None
def get_location_service() -> LocationService:
global _instance
if _instance is None:
_instance = LocationService()
return _instance