diff --git a/blinkenlicht/README.md b/blinkenlicht/README.md index c17abf7..1d0ea39 100644 --- a/blinkenlicht/README.md +++ b/blinkenlicht/README.md @@ -17,9 +17,9 @@ Not quite dogfood-ready yet, but fast approaching. * blinkenlicht.fnl is the module that parses `bar` and `indicator` forms and does all the UI -* metric.fnl is a collection of functions that read metrics (load - average, battery status etc) from the system, for indicators to - display. +* metric/*.fnl is a collection of modules that read metrics (load + average, connectivity, battery status etc) from the system, for + indicators to display. Use the `default.nix` for guidance as to libraries and other setup required - or just use it, of course. @@ -43,10 +43,10 @@ Gtk will find it anyway. Magic. * [X] add some mechanism for indicators that wait for events instead of polling * [X] set indicator background colour (use css for this?) * [ ] update image/label widget instead of destroying -* [ ] allow height customisation +* [ ] allow image height customisation * [X] set poll interval based on indicators' requested intervals * [X] allow indicator to change styles based on status * [ ] add metrics for - - wireless connected/strength + - [X] wireless connected/strength - wwan connected/type (hspa, lte, etc)/signal - cpu % diff --git a/blinkenlicht/bl.fnl b/blinkenlicht/bl.fnl index d0bd88c..40190c7 100644 --- a/blinkenlicht/bl.fnl +++ b/blinkenlicht/bl.fnl @@ -3,10 +3,12 @@ (local {: view} (require :fennel)) (local iostream (require :iostream)) -(local metric (require :metric)) -(local uplink (require :uplink)) (local modem (require :modem)) +(local uplink (require :metric.uplink)) +(local battery (require :metric.battery)) +(local cpustat (require :metric.cpustat)) + (stylesheet "licht.css") (fn battery-icon-codepoint [status percent] @@ -40,26 +42,27 @@ (indicator { :wait-for { :interval (* 1 500) } :refresh - #(let [current (. (metric.cpustat) :iowait) - delta (- current previous) - v (if (> delta 4) "" " ")] - (set previous current) - {:text v}) + (let [stat (cpustat.new)] + #(let [current (. (stat:read) :iowait) + delta (- current previous) + v (if (> delta 4) "" " ")] + (set previous current) + {:text v})) })) - (let [modem (modem.new)] - (indicator { - :wait-for { + (indicator { + :wait-for { :interval (* 4 1000) - } - :refresh + } + :refresh + (let [modem (modem.new)] #(let [{:m3gpp-operator-name operator - :signal-quality quality} (modem:value)] + :signal-quality quality} (modem:read)] {:text (.. operator - ;" " (. quality 1) "dBm" + ;;" " (. quality 1) "dBm" ) - }) - })) + })) + }) (let [uplink (uplink.new) input (iostream.from-descriptor uplink.fd)] @@ -68,7 +71,7 @@ :input [input] } :refresh - #(let [status (uplink:status)] + #(let [status (uplink:read)] (if status {:text (.. (or status.ssid status.name "?") " " @@ -85,15 +88,17 @@ (indicator { :wait-for { :interval (* 1000 10) } :refresh - #(let [{:power-supply-capacity percent - :power-supply-status status} - (metric.battery "axp20x-battery") - icon-code (battery-icon-codepoint - status (tonumber percent))] - {:text - (string.format "%s %d%%" (utf8.char icon-code) percent) - :classes ["battery" (if (< (tonumber percent) 20) "low" "ok")] - }) + (let [battery (battery.new (or (os.getenv "BLINKEN_BATTERY") + "axp20x-battery"))] + #(let [{:power-supply-capacity percent + :power-supply-status status} + (battery.read) + icon-code (battery-icon-codepoint + status (tonumber percent))] + {:text + (string.format "%s %d%%" (utf8.char icon-code) percent) + :classes ["battery" (if (< (tonumber percent) 20) "low" "ok")] + })) }) (indicator { :wait-for { :interval 4000 } diff --git a/blinkenlicht/metric.fnl b/blinkenlicht/metric.fnl deleted file mode 100644 index d963a23..0000000 --- a/blinkenlicht/metric.fnl +++ /dev/null @@ -1,42 +0,0 @@ -(local {: view} (require :fennel)) - -(fn loadavg [] - (with-open [f (io.open "/proc/loadavg" :r)] - (let [line (f:read "*a") - (one five fifteen) (line:match "([%d.]+) +([%d.]+) +([%d.]+)")] - (values (tonumber one) (tonumber five) (tonumber fifteen))))) - -(fn battery [name] - (let [name (.. "/sys/class/power_supply/" name "/uevent")] - (with-open [f (io.open name :r)] - (let [fields {}] - (each [line #(f:read "*l")] - (let [(name value) (line:match "([^=]+)=(.+)")] - (tset fields (: (name:gsub "_" "-") :lower) value))) - fields)))) - -(fn parse-cpu-stat-line [line] - (let [labels [:user :nice :system :idle :iowait - :irq :softirq :steal :guest :guest_nice] - vals (icollect [field (line:gmatch "([%d.]+)")] - (tonumber field))] - (collect [i label (ipairs labels)] - label (. vals i)))) - -(var proc-stat-handle nil) - -(fn cpustat [path] - (if proc-stat-handle - (proc-stat-handle:seek :set 0) - (set proc-stat-handle (io.open "/proc/stat" :r))) - (let [f proc-stat-handle] - (accumulate [ret nil - line #(f:read "*l") ] - (if (= (string.sub line 1 (# "cpu ")) "cpu ") - (parse-cpu-stat-line line) - ret)))) - -{: loadavg - : battery - : cpustat - } diff --git a/blinkenlicht/metric/battery.fnl b/blinkenlicht/metric/battery.fnl new file mode 100644 index 0000000..4263ee3 --- /dev/null +++ b/blinkenlicht/metric/battery.fnl @@ -0,0 +1,15 @@ +(fn battery [name] + (let [name (.. "/sys/class/power_supply/" name "/uevent")] + (with-open [f (io.open name :r)] + (let [fields {}] + (each [line #(f:read "*l")] + (let [(name value) (line:match "([^=]+)=(.+)")] + (tset fields (: (name:gsub "_" "-") :lower) value))) + fields)))) + +{ :new + (fn [name] + { + :read #(battery name) + }) + } diff --git a/blinkenlicht/metric/cpustat.fnl b/blinkenlicht/metric/cpustat.fnl new file mode 100644 index 0000000..23b0f8a --- /dev/null +++ b/blinkenlicht/metric/cpustat.fnl @@ -0,0 +1,24 @@ +(fn parse-cpu-stat-line [line] + (let [labels [:user :nice :system :idle :iowait + :irq :softirq :steal :guest :guest_nice] + vals (icollect [field (line:gmatch "([%d.]+)")] + (tonumber field))] + (collect [i label (ipairs labels)] + label (. vals i)))) + +(fn cpustat [proc-stat-handle] + (let [f proc-stat-handle] + (f:seek :set 0) + (accumulate [ret nil + line #(f:read "*l") + :until ret] + (if (= (string.sub line 1 (# "cpu ")) "cpu ") + (parse-cpu-stat-line line) + ret)))) +{ + :new + #(let [handle (io.open "/proc/stat" :r)] + { + :read #(cpustat handle) + }) + } diff --git a/blinkenlicht/metric/loadavg.fnl b/blinkenlicht/metric/loadavg.fnl new file mode 100644 index 0000000..0397ceb --- /dev/null +++ b/blinkenlicht/metric/loadavg.fnl @@ -0,0 +1,9 @@ +(fn loadavg [] + (with-open [f (io.open "/proc/loadavg" :r)] + (let [line (f:read "*a") + (one five fifteen) (line:match "([%d.]+) +([%d.]+) +([%d.]+)")] + (values (tonumber one) (tonumber five) (tonumber fifteen))))) + +{:new #{ + :read #(loadavg) + }) diff --git a/blinkenlicht/uplink.fnl b/blinkenlicht/metric/uplink.fnl similarity index 91% rename from blinkenlicht/uplink.fnl rename to blinkenlicht/metric/uplink.fnl index deb2a7e..02c8822 100644 --- a/blinkenlicht/uplink.fnl +++ b/blinkenlicht/metric/uplink.fnl @@ -90,13 +90,13 @@ :refresh #(each [_ event (ipairs (sock:event))] (handle-event event)) :fd (sock:fd) - :status (fn [self] - (self:refresh) - (let [defaultroute routes.default - interface (and defaultroute - (. links defaultroute.index))] - (and interface (= interface.running "yes") - (get-network-info interface)))) + :read (fn [self] + (self:refresh) + (let [defaultroute routes.default + interface (and defaultroute + (. links defaultroute.index))] + (and interface (= interface.running "yes") + (get-network-info interface)))) :wait #(sock:poll 1000) :interface (fn [self ifnum] (. links ifnum)) diff --git a/blinkenlicht/modem.fnl b/blinkenlicht/modem.fnl index e930a54..48fd27f 100644 --- a/blinkenlicht/modem.fnl +++ b/blinkenlicht/modem.fnl @@ -5,25 +5,22 @@ (local variant dbus.variant) ;; https://www.freedesktop.org/software/ModemManager/api/latest/ref-dbus.html -(var the-modem-manager nil) + (fn modem-manager [] - (when (not the-modem-manager) - (set the-modem-manager - (dbus.Proxy:new - { - :bus dbus.Bus.SYSTEM - :name "org.freedesktop.ModemManager1" - :interface "org.freedesktop.DBus.ObjectManager" - :path "/org/freedesktop/ModemManager1" - }))) - the-modem-manager) + (dbus.Proxy:new + { + :bus dbus.Bus.SYSTEM + :name "org.freedesktop.ModemManager1" + :interface "org.freedesktop.DBus.ObjectManager" + :path "/org/freedesktop/ModemManager1" + })) ;; this is a function because the path to the modem may change ;; (e.g. due to suspend/resume cycles causing services to be stopped ;; and started) -(fn modem-interface [] - (let [modem-path (next (: (assert (modem-manager)) :GetManagedObjects))] +(fn modem-interface [manager] + (let [modem-path (next (: (assert manager) :GetManagedObjects))] (dbus.Proxy:new { :bus dbus.Bus.SYSTEM @@ -33,10 +30,11 @@ }))) (fn new-modem-status [] - { - :value #(let [m (modem-interface)] - (variant.strip (m:GetStatus))) - }) + (let [manager (modem-manager)] + { + :read #(let [m (modem-interface manager)] + (variant.strip (m:GetStatus))) + })) (comment (let [ctx (: (GLib.MainLoop) :get_context)