Stegrapporter med ledger

Det känns lite barockt att använda ett bokföringsverktyg för just bokföring. Att använda det för att hålla koll på sin stegräkning känns givetvis helt rimligt.

Jag har en fil som definierar olika aktiviteter och hur många steg dessa motsvarar. Dessa data har jag lånat från en godkänd sida. Filen kallar jag aktiviteter.prices, och innehåller aktiviteter jag ibland gör och vad dessa motsvarar, nedan ett litet utdrag:

P 2000-01-01 00:00:00 kilo_steg 1000 steg
P 2000-01-01 00:00:00 h_stå 2400 steg
P 2000-01-01 00:00:00 h_gym_latt 4800 steg

Denna fil läser jag sedan in i min huvudfil, steg.ledger.

include aktiviteter.prices

2019-04-01 * Måndag
    Totalt
    Stå                                           -2 h_stå
    Steg                                        -9.4 kilo_steg

2019-04-02 * Tisdag
    Totalt
    Stå                                           -2 h_stå
    Steg                                        -7.5 kilo_steg

2019-04-03 * Onsdag
    Totalt
    Stå                                           -3 h_stå
    Steg                                      -13556 steg

Notera precis hur många dagar jag använde enheten ”kilo_steg”!

När jag varje månad kollar in hur många steg det har blivit summerar jag med ett anrop till ledger som nedan:

ledger -f steg.ledger --period "this month" --value --monthly reg Totalt

Om jag inte tar med ”Totalt” på slutet får jag en summering av de olika posterna och hur de har bidragit. Sätter jag perioden till ”this year” får jag summerat hur varje månad har blivit totalt, hur många steg/motsvarande steg jag har tagit och så vidare. Det är ingen avancerad funktionalitet direkt, men det är genomtänkt implementerat och enkelt att använda.

Tidrapporter med ledger

Jag har ju en förkärlek till att göra saker dels krångliga, men även att göra dem krångligare med hjälp av Emacs. Nu har jag äntligen hittat ett sätt att blanda in ytterligare ett steg i kedjan, nämligen ledger!

Jag för min tiddagbok för jobbet med hjälp av en textfil som tolkas av programmet ledger, som i sin tur rapporterar på lite olika sätt. Formatet är jätteenkelt, i princip:

i 2019-08-23 08:00:00 Agio:InternTid
o 2019-08-23 12:00:00

Ovan är också en ren lögn – jag är aldrig på jobbet så tidigt! Men ovan är att jag kom in vid 08:00:00 och startade med något internt för jobbet, och gick på lunch vid 12. Jag behöver inte fördefiniera några kunder eller kostnadsställen, utan jag fyller bara på. Formatet är tekniskt sett inte ett rent ledger-format, men ledger har arbetat in stöd för det ändå.

Men, för att göra livet lite lättare skrev jag lite elisp, se nedan. Står jag i en buffer där major-mode är satt till ledger-mode så kallar jag bara på den interaktiva funktionen monotux/check-in och svarar på frågorna, så infogas svaren längst ner i filen. Koden är inte vacker, Men Den Funkar Bra Nog(tm) så vi kör på den!

;; https://emacs.stackexchange.com/a/7150
(defun monotux/re-seq (regexp string)
  "Get a list of all REGEXP matches in a STRING."
  (save-match-data
    (let ((pos 0)
          matches)
      (while (string-match regexp string pos)
        (let ((payee (match-string 1 string)))
          (unless (member payee matches)
            (push (match-string 1 string) matches)))
        (setq pos (match-end 0)))
      matches)))

(defun monotux/get-timeclock-candidates (buffer)
  "Extracts available payee candidates from provided BUFFER."
  (let ((regex-payee "^i [0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} [0-9]\\{2\\}:[0-9]\\{2\\}:[0-9]\\{2\\} \\(.*\\)$"))
    (monotux/re-seq regex-payee buffer)))

(defun monotux/check-in ()
  "Checka in på jobbet!"
  (interactive)
  (when (derived-mode-p 'ledger-mode)
    (let* ((pbuf (current-buffer))
           (clock-in-time (concat (read-string "När kom du till jobbet? (HH:MM) ") ":00"))
           (current-date (format-time-string "%Y-%m-%d"))
           complete-line)
      (dolist (e `("i" ,current-date ,clock-in-time))
        (add-to-list 'complete-line e t))
      (let* ((payee-candidates (monotux/get-timeclock-candidates (buffer-string)))
             (payee-chosen (completing-read "Vilket konto? " payee-candidates)))
        (add-to-list 'complete-line payee-chosen t))
      (goto-char (point-max))
      (insert (concat "\n" (string-join complete-line " ") "\n")))))

(defun monotux/check-out ()
  "Checka ut också."
  (interactive)
  (when (derived-mode-p 'ledger-mode)
    (let ((clock-out-time (concat (read-string "När checkade du ut? (HH:MM) ") ":00"))
          (current-date (format-time-string "%Y-%m-%d"))
          complete-line)
      (dolist (e `("o" ,current-date ,clock-out-time))
        (add-to-list 'complete-line e t))
      (goto-char (point-max))
      (insert (concat (string-join complete-line " ") "\n")))))

För att sammanställa en veckorapport av min tid kan en använda en kommandorad som nedan:

$ ledger -f time.ledger --daily --period "this week" reg
19-Aug-23 - 19-Aug-23    (Agio:InternTid)       4.00h     4.00h

Det roliga kommer givetvis när en har fler rapporter över fler dagar. Men det lämnas som en övning för läsaren att sammanställa själv! 🙂

Hackigt hemmanät

Vi har bråkat lite med vår bredbandsleverantör (haha, Comhem!) på sistone och beslutat att vi inte vill vara kund hos dem mer. Tyvärr finns ingen annan leverantör i huset (Telia har haft huset ”under installation” sedan 2007), så vi föll tillbaka på 4G då vi har rätt bra förutsättningar för det.

Problemet är bara att någon bestämde sig för att inte använda ett vanligt 4G-modem, utan ville Göra Det Själv.

Så en minidator (Asus Tinkerboard S) fanns redan hemma, ett modem och en trådlös basstation behövde inhandlas. Modemet blev ett Huawei E3372 (se tidigare post här om modemet) och routern blev en Netgear R7500 mest p.g.a. fanns begagnad och relativt billig. udev-reglerna från föregående inlägg gick dock att återanvända.

Då jag inte använt iptables för att dirigera paket på ett tag (nftables-hacket förra året räknas inte!) blev det nästan som en pinsam kalldusch att genomföra projektet – hur gjorde en ens? Dessutom var det hejdlöst drygt att ladda ner paketberoenden på min laptop, skicka till enkortsdatorn och installera, upptäcka att fler paket behövdes och så vidare.

När alla paket och paketberoenden väl var på plats konfigurerade jag upp modemet med en network-manager-profil (nmtui var hjälpsamt här, sedan behövde jag komplettera konfigurationsfilen med en pin-kod) och upptäckte att network-manager har dispatcher-skript en kan använda. Nedan är ett skript jag stal från techytalk.info:

#!/bin/bash
 
IF=$1
STATUS=$2

# ppp0 var aktuellt för mig, ändra annars här
if [ "$IF" == "ppp0" ]
then
    case "$2" in
        up)
        logger -s "ppp0 up, create a masquerade party!"
        echo 1 > /proc/sys/net/ipv4/ip_forward
        /sbin/iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE
        /sbin/iptables -A FORWARD -i ppp0 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
        /sbin/iptables -A FORWARD -i eth0 -o ppp0 -j ACCEPT
        ;;
        down)
        logger -s "ppp0, too lazy to remove iptables rules"
        echo 0 > /proc/sys/net/ipv4/ip_forward
        ;;
        pre-up)
        logger -s "NM Script pre-up triggered"
        ;;
        post-down)
        logger -s "NM Script post-down triggered"
        ;;
        *)
        ;;
    esac
fi

Sparade ett liknande skript i /etc/NetworkManager/dispatcher.d/ och gjorde det exekverbart. Att jag inte inkluderat några fler regler än NAT-översättningen är för att modemet just nu agerar brandvägg och gör sin egen NAT-översättning. Det ska gå att byta firmware på modemet men jag har inte haft möjlighet än.

Avslutade med att lägga in en dnsmasq som delar ut IP-adresser samt hanterar DNS-frågor. Kastade även in en pihole-liknande reklam- och spårningsblockering redan i brandväggen.

Pythonhack och NixOS

Jag hackade nyligen ihop ett program som läser in ett API och presenterar detta som en ATOM/RSS-feed. Det blev inte snyggt, men nu kan jag följa en blogg som normalt saknar ett RSS-flöde i min RSS-läsare.

Det trixigaste var att lyckas förstå hur en kör ett program som detta i NixOS, men utan att behöva stöka allt för mycket. Jag ville att skriptet ska köras varje timme och jag ville använda några förpaketerade paketberoenden. I korthet, Nix-Writers fixade biffen!

{ pkgs, ... }:
let
  python-hack = pkgs.writers.writePython3 "pythonhack" {
    libraries = [ pkgs.python3Packages.feedgen pkgs.python3Packages.requests ];
  } (builtins.readFile "/path/to/script.py");
in {
  systemd.services.python-hack = {
    description = "hack hack hack";
    serviceConfig = {
      Type = "oneshot";
      User = "foobar";
      ExecStart = python-hack; # samma namn som i let-blocket
    };
  };
  systemd.timers.python-hack = {
    wantedBy = [ "timers.target" ];
    partOf = [ "python-hack.service" ];
    timerConfig.OnCalendar = "hourly";
  };
}

Det är nog lite hackigt att använda writers-funktionaliteten såhär, det är nog tänkt att användas för att skriva programkod direkt i nix-filen, istället för att bara läsa in källkoden från en fil som här ovan. Men det fungerade, och det var relativt enkelt att sätta ihop.

Huawei E3372 & Linux

Det här är mest en anteckning till den framtida Oscar, och eventuella andra intresserade.

Jag har ett USB-modem jag använder med mina datorer hemma, ett Huawei E3372. Det startar först upp som en CD-enhet för att dela med sig av sina drivrutiner, och behöver sedan instrueras att byta läge till ett vanligt modem.

För detta skapade jag en udev-regel som gör det jobbet åt mig, ersätt sökvägar med vad som är korrekt för din Linux:

ATTR{idVendor}=="12d1", ATTR{idProduct}=="14fe", RUN+="${pkgs.usb_modeswitch}/bin/usb_modeswitch -v 12d1 -p 14fe -V 12d1 -P 1506 -J"

Du kommer behöva programmet usb_modeswitch, som verkar finnas paketerad för de flesta distros.

Alla ID:n och liknande fås av att köra en lsusb. Innan modemet bytt läge:

Bus 002 Device 016: ID 12d1:14dc Huawei Technologies Co., Ltd.

Efter att den bytt läge:

Bus 001 Device 006: ID 12d1:1506 Huawei Technologies Co., Ltd. Modem/Networkcard

För mig startade inte modem-manager (som krävs, och är en del av network-manager) automagiskt, så jag lade till en extra udev-regel för att fixa detta när modemet (i rätt läge) ansluts:

ATTR{idVendor}=="12d1", ATTR{idProduct}=="1506", RUN+="${pkgs.systemd}/bin/systemctl restart modem-manager.service"

Nu behöver jag bara be network-manager att koppla upp sig på 4G. Klart!

Ringtestning

Jag gissar att samma problem lär uppkomma under polarnatten. Det är inte konstigt att folk inte är bekanta med polarnätter och midnattssolen, men åtminstone någon som inte är en junior testare borde veta något om att dagslängd förändras under året och att löjliga edge cases borde testas här.

guetzli

I brist på andra sätt att prokrastinera har jag börjat bekanta mig med verktyget guetzli (”Perceptual JPEG encoder”), som är en riktig rackare på att göra JPEGs mindre i storlek. Som med mycket annan häftig alternativt skrämmande teknik idag är det Google som har finansierat detta projekt.

Originalbilden är ca 4 megapixel, är ca 1.7 megabyte stor i sitt ursprungliga utförande. Den innehåller rätt mycket fin textur och är rätt skarp, trots att den är tagen på frihand. Originalbilden är förresten att ta i, originalbilden är på 24 megapixel och jag har exporterat en nedskalad version som jag sedan använt här.

Jag lät guetzli tugga sig igenom originalbilden på den lägsta tillåtna kvalitetsnivån 84, väntade ut processen och jämförde produkten med originalet.

  • Original: 1.7 megabyte
  • Guetzli, kvalitetsnivå 84: 784 kilobyte
  • Guetzli, kvalitetsnivå 95: 1.2 megabyte

Det är en rätt stor skillnad!

När jag sedan jämförde bilderna vid 100% förstoring tog det mig först en liten stund att se skillnaden — men nu är den rätt solklar för den lägsta inställningen, men inte för den förinställda.


Vad är då nackdelen med guetzli? Tid, samt CPU- och minnesåtgång på maskinen som kör programmet. Det tog 11 minuter att exportera bilden på 84% på min bärbara dator, och när jag lät en lite mer kraftfull maskin göra jobbet tog det fortfarande lång tid:

  • Kvalitet 84, 7 minuter 37 sekunder
  • Kvalitet 87, 8 minuter 6 sekunder
  • Kvalitet 90, 8 minuter 36 sekunder
  • Kvalitet 93, 9 minuter 6 sekunder
  • Kvalitet 95, 9 minuter 31 sekunder

Ovan för en bild på 4 megapixel! Just nu processar samma dator en bild på 24 megapixel vid ett par olika kvalitetsinställningar, men jag tänker inte vänta ut att den körningen går klart innan jag hittar på något annat. 🙂


Jag väntade ut bilden innan jag publicerade detta. Det tog drygt en timme (!) per variant.

  • Originalbilden, ca 12 megabyte
  • Kvalitetsnivå 95, 7.4 megabyte
  • Kvalitetsnivå 90, 4.8 megabyte
  • Kvalitetsnivå 84, 4 megabyte

Jag vet inte vad som är rimligast av nivåerna ovan. Jag har bara märkt att mina bilder just nu är rätt stora och att jag borde minska ner dessa lite. Jag blev lite nyfiken på att sätta upp ’objektiva’ kvalitetsmått likt det som beskrivs i guetzlis Wikipedia-sida...men det är ett äventyr för en annan kväll.