---
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.
