standardise interface to metrics

call as follows:

(let [m  (metricname.new)]
  (m:read)
  (m:read)
   ...
   )
phoen
Daniel Barlow 2022-04-07 10:00:49 +01:00
parent b6a8048c8b
commit 463c1eea03
8 changed files with 106 additions and 97 deletions

View File

@ -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 %

View File

@ -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 }

View File

@ -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
}

View File

@ -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)
})
}

View File

@ -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)
})
}

View File

@ -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)
})

View File

@ -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))

View File

@ -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)