sane-weather: init

for now, all it does is print the current temperature; no caching
This commit is contained in:
2023-08-23 11:14:13 +00:00
parent cc6f33b928
commit f945dc42fa
4 changed files with 155 additions and 8 deletions

View File

@@ -0,0 +1,36 @@
{ stdenv
, glib-networking
, gobject-introspection
, libgweather
, python3
, wrapGAppsNoGuiHook
}:
let
pyEnv = python3.withPackages (ps: [
ps.pygobject3
]);
in
stdenv.mkDerivation {
pname = "sane-weather";
version = "0.1";
src = ./.;
nativeBuildInputs = [
gobject-introspection
wrapGAppsNoGuiHook
];
buildInputs = [
glib-networking
libgweather
pyEnv
];
installPhase = ''
runHook preInstall
mkdir -p "$out/bin"
cp sane-weather "$out/bin"
runHook postInstall
'';
}

View File

@@ -0,0 +1,105 @@
#!/usr/bin/env python3
# to get GWeather logging, run with
# `G_MESSAGES_DEBUG=GWeather`
import argparse
import code
import gi
import logging
gi.require_version('GWeather', '4.0')
from gi.repository import GLib
from gi.repository import GWeather
logger = logging.getLogger(__name__)
class WeatherSource:
def __init__(self):
self.info = GWeather.Info()
self.info.set_application_id('org.uninsane.sane-weather')
self.info.set_contact_info('contact@uninsane.org')
self.world = GWeather.Location.get_world()
def query_loc(self, loc: GWeather.Location) -> None:
logger.debug(f"querying: {loc.get_coords()}")
self.info.set_location(loc)
self.info.update()
def try_get_celcius(self) -> float | None:
valid, temp = self.info.get_value_temp(GWeather.TemperatureUnit.CENTIGRADE)
logger.debug(f"try_get_celcius: valid={valid}, temp={temp}")
if not valid: temp = None
return temp
class QueryOp:
def __init__(self, loc: GWeather.Location):
self.loc = loc
class PrintTempOp:
pass
class ExitOp:
pass
class TopLevel:
def __init__(self):
self._loop = GLib.MainLoop()
self.source = WeatherSource()
self.work_queue = []
def enqueue(self, op) -> None:
self.work_queue.append(op)
def run(self) -> None:
self.enqueue(ExitOp())
GLib.idle_add(self.poll)
self._loop.run()
def poll(self) -> bool:
work = self.work_queue[0]
if isinstance(work, QueryOp):
del self.work_queue[0]
self.source.query_loc(work.loc)
elif isinstance(work, PrintTempOp):
temp = self.source.try_get_celcius()
if temp is not None:
del self.work_queue[0]
print(f"{int(temp)} C")
elif isinstance(work, ExitOp):
logger.debug("quitting GLib MainLoop")
self._loop.quit()
else:
assert False, f"unknown work: {work}"
return True # re-queue this idle fn
def main():
logging.basicConfig()
parser = argparse.ArgumentParser(description="acquire weather information for user display")
parser.add_argument('--interactive', action='store_true', help='drop into a REPL instead of doing anything, for debugging')
parser.add_argument('--verbose', action='store_true', help='enable verbose logging') #< only applies immediately to this app; use `G_MESSAGES_DEBUG=all` for more verbosity
args = parser.parse_args()
if args.verbose:
logger.setLevel(logging.DEBUG)
toplevel = TopLevel()
# for now, hardcoded; four-letter station code is that from METAR:
# - <https://aviationweather.gov/metar>
here = GWeather.Location.find_by_station_code(toplevel.source.world, 'KSEA')
if args.interactive:
code.interact(local=dict(**globals(), **locals()))
return
toplevel.enqueue(QueryOp(here))
toplevel.enqueue(PrintTempOp())
toplevel.run()
if __name__ == '__main__':
main()

View File

@@ -1,14 +1,19 @@
{ pkgs
, bash
, lib
, makeWrapper
, python3
, stdenv
, zsh
}:
let
inherit (builtins) attrNames attrValues concatStringsSep foldl' map typeOf;
inherit (lib) concatMapAttrs;
bash' = bash;
pkgs' = pkgs;
python3' = python3;
zsh' = zsh;
# create an attrset of
# <name> = expected string in the nix-shell invocation
# <value> = package to provide
@@ -74,33 +79,33 @@ in rec {
);
# `mkShell` specialization for `nix-shell -i bash` scripts.
mkBash = { pname, pkgs ? {}, srcPath ? pname, ...}@attrs:
mkBash = { pname, pkgs ? {}, srcPath ? pname, bash ? bash', ...}@attrs:
let
pkgsAsAttrs = pkgsToAttrs "" pkgs' pkgs;
pkgsEnv = attrValues pkgsAsAttrs;
pkgExprs = attrNames pkgsAsAttrs;
in mkShell ({
inherit pkgsEnv pkgExprs;
interpreter = "${pkgs'.bash}/bin/bash";
} // (removeAttrs attrs [ "pkgs" ])
interpreter = "${bash}/bin/bash";
} // (removeAttrs attrs [ "bash" "pkgs" ])
);
# `mkShell` specialization for `nix-shell -i zsh` scripts.
mkZsh = { pname, pkgs ? {}, srcPath ? pname, ...}@attrs:
mkZsh = { pname, pkgs ? {}, srcPath ? pname, zsh ? zsh', ...}@attrs:
let
pkgsAsAttrs = pkgsToAttrs "" pkgs' pkgs;
pkgsEnv = attrValues pkgsAsAttrs;
pkgExprs = attrNames pkgsAsAttrs;
in mkShell ({
inherit pkgsEnv pkgExprs;
interpreter = "${pkgs'.zsh}/bin/zsh";
} // (removeAttrs attrs [ "pkgs" ])
interpreter = "${zsh}/bin/zsh";
} // (removeAttrs attrs [ "pkgs" "zsh" ])
);
# `mkShell` specialization for invocations of `nix-shell -p "python3.withPackages (...)"`
# pyPkgs argument is parsed the same as pkgs, except that names are assumed to be relative to `"ps"` if specified in list form.
# TODO: rename to `mkPython3` for consistency with e.g. `mkBash`
mkPython3Bin = { pname, pkgs ? {}, pyPkgs ? {}, srcPath ? pname, ... }@attrs:
mkPython3Bin = { pname, pkgs ? {}, pyPkgs ? {}, srcPath ? pname, python3 ? python3', ... }@attrs:
let
pyEnv = python3.withPackages (ps: attrValues (
pkgsToAttrs "ps." ps pyPkgs
@@ -118,6 +123,6 @@ in rec {
inherit pkgsEnv pkgExprs;
interpreter = pyEnv.interpreter;
interpreterName = "python3";
} // (removeAttrs attrs [ "pkgs" "pyPkgs" ])
} // (removeAttrs attrs [ "pkgs" "pyPkgs" "python3" ])
);
}

View File

@@ -51,6 +51,7 @@ let
mx-sanebot = callPackage ./additional/mx-sanebot { };
rtl8723cs-firmware = callPackage ./additional/rtl8723cs-firmware { };
sane-scripts = lib.recurseIntoAttrs (callPackage ./additional/sane-scripts { });
sane-weather = callPackage ./additional/sane-weather { };
static-nix-shell = callPackage ./additional/static-nix-shell { };
sublime-music-mobile = callPackage ./additional/sublime-music-mobile { };
sxmo-utils = callPackage ./additional/sxmo-utils { };