Ajoute un test 'Power Supply' au Self Test pour détecter une sous-tension du Pi (cause fréquente de capteurs USB instables, corruptions SD, reboots). Endpoint launcher.php?type=throttled + script power/get_throttled.py (lancé via sudo python3, déjà whitelisté — pas de modif sudoers). Affiché en tête des résultats et dans le rapport copiable. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
83 lines
2.8 KiB
Python
83 lines
2.8 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
python3 /var/www/nebuleair_pro_4g/power/get_throttled.py
|
|
|
|
Lit l'état d'alimentation du Raspberry Pi via `vcgencmd get_throttled` et
|
|
renvoie un JSON décodé. Utilisé par le Self Test (launcher.php?type=throttled)
|
|
pour détecter une sous-tension (cause fréquente de capteurs USB instables,
|
|
corruptions SD, reboots).
|
|
|
|
Doit tourner en root (vcgencmd a besoin de /dev/vcio) : appelé via
|
|
`sudo /usr/bin/python3 ...` depuis launcher.php.
|
|
|
|
Bits de get_throttled (cf. doc Raspberry Pi) :
|
|
0 : sous-tension active maintenant
|
|
1 : freq ARM bridée maintenant
|
|
2 : throttling actif maintenant
|
|
3 : limite temperature douce active maintenant
|
|
16 : sous-tension survenue depuis le boot
|
|
17 : bridage freq ARM survenu depuis le boot
|
|
18 : throttling survenu depuis le boot
|
|
19 : limite temperature douce survenue depuis le boot
|
|
"""
|
|
|
|
import json
|
|
import subprocess
|
|
|
|
|
|
def main():
|
|
try:
|
|
raw = subprocess.check_output(
|
|
['/usr/bin/vcgencmd', 'get_throttled'],
|
|
stderr=subprocess.STDOUT,
|
|
timeout=5,
|
|
).decode('utf-8', errors='ignore').strip()
|
|
except FileNotFoundError:
|
|
print(json.dumps({"available": False, "error": "vcgencmd introuvable (pas un Raspberry Pi ?)"}))
|
|
return
|
|
except Exception as e:
|
|
print(json.dumps({"available": False, "error": str(e)}))
|
|
return
|
|
|
|
# Sortie attendue : "throttled=0x50000"
|
|
if '=' not in raw:
|
|
print(json.dumps({"available": False, "error": f"sortie inattendue: {raw}"}))
|
|
return
|
|
|
|
hex_str = raw.split('=', 1)[1].strip()
|
|
try:
|
|
value = int(hex_str, 16)
|
|
except ValueError:
|
|
print(json.dumps({"available": False, "error": f"valeur illisible: {raw}"}))
|
|
return
|
|
|
|
flags = {
|
|
"under_voltage_now": bool(value & 0x1),
|
|
"arm_freq_capped_now": bool(value & 0x2),
|
|
"throttled_now": bool(value & 0x4),
|
|
"soft_temp_limit_now": bool(value & 0x8),
|
|
"under_voltage_occurred": bool(value & 0x10000),
|
|
"arm_freq_capped_occurred": bool(value & 0x20000),
|
|
"throttling_occurred": bool(value & 0x40000),
|
|
"soft_temp_limit_occurred": bool(value & 0x80000),
|
|
}
|
|
|
|
# Niveau de gravite pour le Self Test
|
|
if flags["under_voltage_now"] or flags["throttled_now"]:
|
|
status = "critical"
|
|
message = "Sous-tension ACTIVE — alimentation 5V insuffisante (alim/cable a remplacer)"
|
|
elif flags["under_voltage_occurred"] or flags["throttling_occurred"]:
|
|
status = "warning"
|
|
message = "Sous-tension survenue depuis le demarrage — verifier alim 5V / cable USB"
|
|
else:
|
|
status = "ok"
|
|
message = "Alimentation OK"
|
|
|
|
result = {"available": True, "raw": hex_str, "value": value, "status": status, "message": message}
|
|
result.update(flags)
|
|
print(json.dumps(result))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|