Bonjour,
Suite à changement de ma box SFR, ce que j’avais mis en place ne fonctionnait plus.
Et je suis tombé sur ça.
Puis, avec l’aide précieuse de mon “pote” Claude j’ai enfin pu mettre au point ce script python, statsbox.py :
#!/usr/bin/env python3
import hashlib
import hmac
import requests
import json
import sys
import argparse
# Configuration par défaut
BOX_URL = "http://xx.xx.xx.xx"
USERNAME = "xxxxxxx"
PASSWORD = "xxxxxxxx"
def login(base_url, username, password):
"""Login to the box and return authenticated session"""
s = requests.Session()
# Get nonce
r = s.get(base_url + "/uxfwk.session.loader.js")
sessionid = None
nonce = None
for line in r.text.split('\n'):
if 'sessionid' in line.lower():
sessionid = line.split(':')[1].strip().strip('"').strip("'").strip(',').strip(';').strip()
#print(f"DEBUG: Found sessionid line: {line.strip()}", file=sys.stderr)
if 'nonce' in line.lower():
# Parse: nonce: 'ABC123',
raw = line.split(':')[1].strip() # " 'ABC123',"
nonce = raw.strip(',').strip(';').strip().strip('"').strip("'").strip() # "ABC123"
#print(f"DEBUG: Found nonce line: {line.strip()}", file=sys.stderr)
#print(f"DEBUG: Raw nonce after split: '{raw}'", file=sys.stderr)
#print(f"DEBUG: Parsed sessionid = {sessionid}", file=sys.stderr)
#print(f"DEBUG: Parsed nonce = '{nonce}'", file=sys.stderr)
#print(f"DEBUG: Nonce length = {len(nonce) if nonce else 0}", file=sys.stderr)
if not nonce:
raise Exception("Failed to get nonce")
# Compute hash
sha256_usr = hashlib.sha256(username.encode('UTF-8')).hexdigest()
hmac_usr = hmac.new(bytes(nonce, 'UTF-8'), digestmod='sha256')
hmac_usr.update(sha256_usr.encode())
sha256_pass = hashlib.sha256(password.encode('UTF-8')).hexdigest()
hmac_pass = hmac.new(bytes(nonce, 'UTF-8'), digestmod='sha256')
hmac_pass.update(sha256_pass.encode())
credentials = hashlib.sha256(bytes(hmac_usr.hexdigest() + hmac_pass.hexdigest(), 'UTF-8')).hexdigest()
#print(f"DEBUG: Credentials hash = {credentials}", file=sys.stderr)
s.headers['Authorization'] = 'Digest ' + credentials
# Get XSRF token
r = s.get(base_url + "/index.html")
# Debug: afficher ce qu'on reçoit
#print(f"DEBUG: Response status = {r.status_code}", file=sys.stderr)
#print(f"DEBUG: Response headers = {dict(r.headers)}", file=sys.stderr)
#print(f"DEBUG: Cookies = {[(c.name, c.value) for c in s.cookies]}", file=sys.stderr)
if 'X-XSRF-TOKEN' in r.headers:
s.headers['X-XSRF-TOKEN'] = r.headers['X-XSRF-TOKEN']
#print(f"DEBUG: XSRF token found in headers", file=sys.stderr)
elif 'XSRF-TOKEN' in s.cookies:
s.headers['X-XSRF-TOKEN'] = s.cookies['XSRF-TOKEN']
#print(f"DEBUG: XSRF token found in cookies", file=sys.stderr)
else:
print(f"ERROR: XSRF token not found!", file=sys.stderr)
raise Exception("Failed to get XSRF token")
return s
def get_wan_stats(session, base_url):
"""Get WAN statistics (rx_b and tx_b)"""
r = session.get(base_url + '/ss-json/fgw.wan/fgw.wanstatistics.json')
data = json.loads(r.text)
if 'statistics' in data and len(data['statistics']) > 0:
stats = data['statistics'][0]
return {
'rx_bytes': stats.get('rx_b', 0),
'tx_bytes': stats.get('tx_b', 0)
}
return None
def logout(session, base_url):
"""Logout from the box"""
try:
session.get(base_url + '/logout.cmd')
except:
pass # Ignore logout errors
def main():
# Parse command line arguments
parser = argparse.ArgumentParser(description='Get SFR Box WAN statistics')
parser.add_argument('-u', '--user', default=USERNAME, help='Username (default: admin)')
parser.add_argument('-p', '--password', default=PASSWORD, help='Password')
parser.add_argument('--url', default=BOX_URL, help='Box URL (default: http://192.168.1.1)')
args = parser.parse_args()
try:
# Login
session = login(args.url, args.user, args.password)
# Get current stats
stats = get_wan_stats(session, args.url)
if not stats:
logout(session, args.url)
print("ERROR: Could not retrieve WAN statistics", file=sys.stderr)
sys.exit(1)
# Logout
logout(session, args.url)
# Output raw counters in JSON format
print(json.dumps({
'rx_bytes': stats['rx_bytes'],
'tx_bytes': stats['tx_bytes']
}))
except Exception as e:
print(f"ERROR: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == '__main__':
main()
A placer au même endroit que vos autres scripts python que vous utilisez déjà [peut-être …] (pour mon cas, c’est dans /config/python_scripts).
Et n’oubliez pas de placer les bonnes valeurs dans les variables BOX_URL, USERNAME et PASSWORD.
Il faut ajouter ceci dans votre config.yaml :
command_line:
# récup des stats WAN rx et tx de la box SFR
- sensor:
name: "box_wan_stats"
command: "python /config/python_scripts/statsbox.py"
value_template: "OK"
json_attributes:
- rx_bytes
- tx_bytes
scan_interval: 60 # Toutes les minutes
template:
# récup du débit IN & OUT du lien WAN de la box SFR
- sensor:
- name: "BOX WAN RX Bytes"
unique_id: box_wan_rx_bytes
unit_of_measurement: "B"
device_class: data_size
state_class: total_increasing
icon: mdi:download-network
state: "{{ state_attr('sensor.box_wan_stats', 'rx_bytes') | int(0) }}"
- name: "BOX WAN TX Bytes"
unique_id: box_wan_tx_bytes
unit_of_measurement: "B"
device_class: data_size
state_class: total_increasing
icon: mdi:upload-network
state: "{{ state_attr('sensor.box_wan_stats', 'tx_bytes') | int(0) }}"
En option, à des fin de test et debug :
shell_command:
get_box_stats: "python3 /config/python_scripts/statsbox.py >/config/tmp/box_stats.json 2>/config/tmp/box_stats_error.log"
N’oubliez pas de créer le répertoire tmp dans /config.
Puis un redémarrage de HA pour faire apparaitre 3 nouveaux sensors :
- sensor.box_wan_stats
- sensor.box_wan_rx_bytes
- sensor.box_wan_tx_bytes
Pour info, les valeurs rx et tx représentent le cumul d’octets envoyés ou reçus par l’interface fibre. Et au delà de 4Go, ça repart depuis 0. Là, le relevé de ce cumul est donc réalisé toutes les 5 minutes.
Ensuite, à vous de décider du moyen pour tracer les courbes. Pour ma part, tout va dans InfluxDB v2 puis Grafana fait le boulot.
Le script python fonctionne bien avec un box SFR fibre v7, modèle GR140CG, avec le firmware 3ENT010301r008. Une éventuelle maj de ce dernier pourrait le rendre inopérant.
D’autres infos peuvent être récupérées, en fait toutes celles que vous avez via l’interface web de la box. Mais j’ai aussi l’intégration SFR qui complète.
A bientôt.