--- name: collect_battery_metrics kind: function lang: go domain: infra version: "1.0.0" purity: impure signature: "func CollectBatteryMetrics() ([]PromSample, error)" description: "Recolecta metricas de bateria de un dispositivo Android via el comando termux-battery-status (paquete termux-api) y las devuelve como slice de PromSample con nombres estilo node_exporter: porcentaje, temperatura, estado de carga (booleano), corriente en microamperios y una serie informativa node_battery_health_info con labels health/status/plugged. Best-effort y multiplataforma: en nodos sin termux-battery-status (Linux normales) es un no-op que devuelve slice vacio y error nil; solo emite samples cuando el comando existe y responde JSON valido. El comando corre con timeout de 5s via context." tags: [prometheus, metrics, node-exporter, battery, termux, android, fleet-metrics, infra, monitoring] uses_functions: [] uses_types: ["PromSample_go_infra"] returns: [] returns_optional: false error_type: "error_go_core" imports: ["context", "encoding/json", "os/exec", "time"] params: [] output: "slice de PromSample con metricas de bateria. node_battery_percent (0-100), node_battery_temp_celsius, node_battery_charging (1 si carga/lleno/enchufado, si no 0), node_battery_current_ua (microamperios, negativo al descargar) y node_battery_health_info{health,status,plugged} con value 1. En nodos sin termux-api devuelve slice vacio. Error nil siempre en condiciones normales: la funcion traga los fallos de ejecucion/parseo como no-op (slice vacio)." tested: true tests: - "TestCollectBatteryMetrics_ParseDischarging" - "TestCollectBatteryMetrics_ParseCharging" - "TestCollectBatteryMetrics_ParsePluggedNotUnplugged" - "TestCollectBatteryMetrics_ParseFull" - "TestCollectBatteryMetrics_InvalidJSON" test_file_path: "functions/infra/collect_battery_metrics_test.go" file_path: "functions/infra/collect_battery_metrics.go" --- ## Ejemplo ```go samples, err := CollectBatteryMetrics() if err != nil { log.Fatal(err) } // En un Linux normal samples sera vacio (no-op); en Android/Termux trae // node_battery_percent, node_battery_temp_celsius, node_battery_charging, etc. // Componer con el resto del capability group fleet-metrics: host, _ := CollectHostMetrics() all := append(host, samples...) body := FormatPromExposition(all, time.Now().UnixMilli()) err = PushPromRemote( "https://metrics-xxxx.organic-machine.com/api/v1/import/prometheus", "user", "pass", body, map[string]string{"instance": "pixel-phone"}, ) ``` ## Cuando usarla Cuando un nodo de la flota es un movil Android con Termux + termux-api y quieres exponer la salud de su bateria como metricas Prometheus para push a un backend remoto (VictoriaMetrics, Mimir). Llamala junto a `collect_host_metrics_go_infra` en el loop del agente de monitorizacion push y concatena los slices: en moviles añade las series de bateria, en el resto de nodos no aporta nada (no-op seguro), asi puedes usar el MISMO agente en toda la flota sin ramas por plataforma. ## Gotchas - **Solo produce datos en Termux/Android con termux-api instalado**: necesita el binario `termux-battery-status` (paquete `termux-api` + la app Termux:API). En cualquier otro nodo (Linux de escritorio, VPS, macOS) `exec.LookPath` falla y la funcion es un no-op que devuelve `[]PromSample{}, nil`. No es un error: simplemente no hay bateria que reportar. - **No devuelve error nunca en condiciones normales**: por diseño best-effort, tanto el binario ausente como un comando fallido (timeout, permisos) o un JSON invalido se tragan como slice vacio. La firma mantiene `error` por convencion de impureza, pero el caller no necesita ramificar por error de plataforma. - **Timeout de 5s**: usa `exec.CommandContext` con `context.WithTimeout`. Si termux-api se cuelga, la llamada aborta a los 5s y devuelve no-op. - **node_battery_current_ua puede ser negativo**: convencion de Android — corriente negativa = descarga, positiva = carga. Se reporta tal cual (microamperios). - **node_battery_charging es heuristico**: vale 1 si `status` es `CHARGING` o `FULL`, o si `plugged != "UNPLUGGED"`. Cubre el caso de estar enchufado sin cargar activamente (ej. `NOT_CHARGING` con cargador conectado). - **No incluye la label `instance`**: igual que el resto de colectores del grupo, esa la añade `push_prom_remote_go_infra` via extra_label en el push. - **El parseo esta factorizado** en `parseBatteryJSON` (funcion pura interna) para poder testear los samples sin ejecutar termux-battery-status real.