prometheus-node-exporter-lua: add modemmanager exporter
authorJean-Laurent Girod <redacted>
Wed, 6 May 2026 16:32:17 +0000 (16:32 +0000)
committerEtienne Champetier <redacted>
Sun, 24 May 2026 13:26:21 +0000 (09:26 -0400)
Add a Prometheus collector for ModemManager that exports cellular modem
signal metrics via mmcli. Supports multiple modems (labeled by D-Bus
object path), exports overall signal quality and detailed per-technology
signal parameters (LTE, NR5G, UMTS, GSM, CDMA, ...).

Requires signal refresh to be enabled on the modem:
  mmcli -m <id> --signal-setup=<interval_seconds>

Tested on: ath79/generic, GL.inet GL-X300B, OpenWrt 23.05.5

Co-authored-by: Claude <redacted>
Signed-off-by: Jean-Laurent Girod <redacted>
utils/prometheus-node-exporter-lua/Makefile
utils/prometheus-node-exporter-lua/files/usr/lib/lua/prometheus-collectors/modemmanager.lua [new file with mode: 0644]

index 31583a6cf8df13f4cb78f7c0a1375ae120dd9cff..e559a05a99ffb87aede4bb76bc9544693f5f878b 100644 (file)
@@ -4,7 +4,7 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=prometheus-node-exporter-lua
-PKG_VERSION:=2025.11.22
+PKG_VERSION:=2026.05.06
 PKG_RELEASE:=1
 
 PKG_MAINTAINER:=Etienne CHAMPETIER <champetier.etienne@gmail.com>
@@ -279,6 +279,17 @@ define Package/prometheus-node-exporter-lua-nft-counters/install
        $(INSTALL_DATA) ./files/usr/lib/lua/prometheus-collectors/nft_counters.lua $(1)/usr/lib/lua/prometheus-collectors/
 endef
 
+define Package/prometheus-node-exporter-lua-modemmanager
+  $(call Package/prometheus-node-exporter-lua/Default)
+  TITLE+= (modemmanager collector)
+  DEPENDS:=prometheus-node-exporter-lua +modemmanager +lua-cjson
+endef
+
+define Package/prometheus-node-exporter-lua-modemmanager/install
+       $(INSTALL_DIR) $(1)/usr/lib/lua/prometheus-collectors
+       $(INSTALL_DATA) ./files/usr/lib/lua/prometheus-collectors/modemmanager.lua $(1)/usr/lib/lua/prometheus-collectors/
+endef
+
 $(eval $(call BuildPackage,prometheus-node-exporter-lua))
 $(eval $(call BuildPackage,prometheus-node-exporter-lua-bmx7))
 $(eval $(call BuildPackage,prometheus-node-exporter-lua-dawn))
@@ -300,3 +311,4 @@ $(eval $(call BuildPackage,prometheus-node-exporter-lua-realtek-poe))
 $(eval $(call BuildPackage,prometheus-node-exporter-lua-mwan3))
 $(eval $(call BuildPackage,prometheus-node-exporter-lua-ethtool))
 $(eval $(call BuildPackage,prometheus-node-exporter-lua-nft-counters))
+$(eval $(call BuildPackage,prometheus-node-exporter-lua-modemmanager))
diff --git a/utils/prometheus-node-exporter-lua/files/usr/lib/lua/prometheus-collectors/modemmanager.lua b/utils/prometheus-node-exporter-lua/files/usr/lib/lua/prometheus-collectors/modemmanager.lua
new file mode 100644 (file)
index 0000000..8f9a83c
--- /dev/null
@@ -0,0 +1,48 @@
+local cjson = require "cjson"
+
+local function scrape()
+  local file, output, data
+
+  file = io.popen("mmcli -L -J", 'r')
+  if not file then return end
+  output = file:read('*all')
+  file:close()
+
+  local ok, modem_list = pcall(cjson.decode, output)
+  if not ok or not modem_list["modem-list"] then return end
+
+  for _, modem in ipairs(modem_list["modem-list"]) do
+
+    file = io.popen("mmcli -m " .. modem .. " -J", 'r')
+    if file then
+      output = file:read('*all')
+      file:close()
+
+      ok, data = pcall(cjson.decode, output)
+      if ok and data["modem"] then
+        local quality = data["modem"]["generic"]["signal-quality"]["value"]
+        metric("modemmanager_signal_quality", "gauge", {modem=modem}, tonumber(quality))
+      end
+    end
+
+    file = io.popen("mmcli -m " .. modem .. " --signal-get -J", 'r')
+    if file then
+      output = file:read('*all')
+      file:close()
+
+      ok, data = pcall(cjson.decode, output)
+      if ok and data["modem"] and data["modem"]["signal"] then
+        for tech, values in pairs(data["modem"]["signal"]) do
+          for metric_name, value in pairs(values) do
+            local num = tonumber(value)
+            if num then
+              metric("modemmanager_signal_" .. tech .. "_" .. metric_name, "gauge", {modem=modem}, num)
+            end
+          end
+        end
+      end
+    end
+  end
+end
+
+return { scrape = scrape }
git clone https://git.99rst.org/PROJECT