Skip to content

Instantly share code, notes, and snippets.

@psa-jforestier
Last active June 20, 2025 11:42
Show Gist options
  • Save psa-jforestier/1f8e99b6be687c26d5e6473633ba62ab to your computer and use it in GitHub Desktop.
Save psa-jforestier/1f8e99b6be687c26d5e6473633ba62ab to your computer and use it in GitHub Desktop.
Script Python pour recupérer des info de la Box SFR 7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
Inspired by https://lafibre.info/sfr-les-news/nb6vac-et-api/
Compatible with SFR BOX FIBRE 7 (Router name : GR140CG)
Usage : python3 rbox_status.ph -u admin -p password
'''
import hashlib
import hmac
import requests
import re
import time
import datetime
import argparse
import pprint
import json
import sys
import os
#import xml.etree.ElementTree as ET
def DEBUG(text: str):
if (VERBOSE_LEVEL==True):
print(text)
def _compute_hash(token: str, value: str) -> str:
"""Compute single value hash."""
hash = hashlib.sha256(value.encode()).hexdigest()
return hmac.new(token.encode(), hash.encode(), hashlib.sha256).hexdigest()
def compute_hash(token: str, username: str, password: str) -> str:
# the original code will not work for the web interface.
# For the API, it is hash(u)+hash(p),
# but the web interface use a hash(hash(u)+hash(p))
"""Compute full username/password hash."""
hmac_username = _compute_hash(token, username)
hmac_password = _compute_hash(token, password)
return f"{hmac_username}{hmac_password}"
def Box_GetJSON(session, url):
r = session.get(url)
j = json.loads(r.text)
return j
SECTION_INC=0
def Box_PrintBeginSection(fmt, name):
global SECTION_INC
global output
f = sys.stdout if output == None else open(output, "a")
if (fmt == 'txt'):
print(">" * (3*SECTION_INC) + ">>> " + name, file=f)
SECTION_INC = SECTION_INC + 1
def Box_PrintEndSection(fmt, name):
global SECTION_INC
global output
f = sys.stdout if output == None else open(output, "a")
if (fmt == 'txt'):
print("", file=f)
SECTION_INC = SECTION_INC - 1
def Box_PrintInfo(fmt, info, value):
global output
f = sys.stdout if output == None else open(output, "a")
if (fmt == 'txt'):
print(info+": "+value.strip(), file=f)
return
def parse_table(s):
# Use regex to find all rows in the string
rows = re.findall(r'<tr[^>]*>(.*?)</tr>', s, re.DOTALL)
result = []
for row in rows:
# Find all cells in the row
cells = re.findall(r'<td[^>]*>(.*?)</td>', row, re.DOTALL)
# Clean up the cell data by stripping whitespace and HTML tags
cleaned_cells = [re.sub(r'<.*?>', '', cell).strip() for cell in cells]
result.append(cleaned_cells)
return result
def XMLAttributesToObject(string, node):
doc = ET.fromstring(string.strip())
attr = {}
for item in doc.findall(node):
for attr_name, attr_value in item.attrib.items():
attr[attr_name] = attr_value
return attr
def XMLAttributesToObjects(string, node):
doc = ET.fromstring(string.strip())
obj=[]
attr = {}
for item in doc.findall(node):
attr = {}
for attr_name, attr_value in item.attrib.items():
attr[attr_name] = attr_value
obj.append(attr)
return obj
def extractCookiesFromString(s):
# Split the string into individual key-value pairs using "; " as the separator
key_value_pairs = s.split('; ')
# Initialize an empty dictionary to hold the key-value pairs
result_dict = {}
# Process each key-value pair
for pair in key_value_pairs:
# Split on the first '=' to separate the key and value
key, value = pair.split('=', 1)
result_dict[key.strip()] = value.strip() # Add to the dictionary with stripped keys and values
return result_dict
global output
parser = argparse.ArgumentParser(
prog = 'SFR Box status',
description = 'Print advanced status of the SFR internet box.'
)
parser.add_argument('-u', '--user',
action='store',
help='username of the internet box')
parser.add_argument('-p', '--pass',
action='store',
help='password of the username')
parser.add_argument('url',
help='url of the internet box (default: %(default)s)',
default='http://192.168.1.1', nargs='?',
action='store' )
parser.add_argument('--minimal',
action='store_true',
default=False,
help='retreive minimal information')
parser.add_argument('--bandwidth',
action='store_true',
default=False,
help='compute bandwidth based on last report stored in --output FILE')
parser.add_argument('--extra',
action='store',
type=lambda s: [str(item) for item in s.split(',')],
default=None,
help='retreive extra informations. Use a , separated list within "portForwarding"')
parser.add_argument('--call',
action='store',
default=None,
help='call directly the url. with proper authentication, you can retreive directly the json. use "http://192.168.1.1/ss-json/fgw.voice/fgw.voice.callhistory.json"')
parser.add_argument('-f', '--format',
help='output format : txt, csv, html, json (default: %(default)s)',
default='txt',
action='store')
parser.add_argument('-o', '--output',
help='output to a file',
action='store')
parser.add_argument('--token',
help='Use this token',
action='store', nargs='?')
parser.add_argument('--verbose',
action='store_true',
help='display all transaction and internal computation')
args = parser.parse_args()
password = getattr(args, 'pass') # args.pass trigger an error
username=args.user
VERBOSE_LEVEL=args.verbose
timeout = 3
DEBUG("Using box located at " + args.url)
token=args.token
fmt=args.format
output=args.output
if (args.bandwidth == True and output == None):
print("To calculate badwidth, you must indicate the last report with --output FILE")
quit()
try:
if (args.bandwidth == True):
# Open last report and compute bandwidth
DEBUG("open "+output+" to compute bandwidth")
last_report_time = None
last_wanRx = None
last_wanTx = None
try:
with open(output, 'r') as f:
for line in f:
if line.startswith('current_time:'):
last_report_time = datetime.datetime.fromisoformat(line.split(': ')[1].strip())
elif line.startswith('wanRx:'):
last_wanRx = int(line.split(': ')[1].strip())
elif line.startswith('wanTx:'):
last_wanTx = int(line.split(': ')[1].strip())
os.remove(output)
except FileNotFoundError:
last_report_time = None
DEBUG("Last report on "+str(last_report_time))
DEBUG(" last_wanRx "+str(last_wanRx))
DEBUG(" last_wanTx "+str(last_wanTx))
Box_PrintBeginSection(fmt, "Login")
T = time.time()
s = requests.Session() # will be use to keep cookie
# Get the nonce
url = args.url + "/uxfwk.session.loader.js"
r = s.get(url=url)
sessionid = s.cookies['SESSIONID']
DEBUG("sessionid="+sessionid)
#items=re.findall(".*nonce:.*$",r.text,re.MULTILINE)
match=re.search(r"\bnonce:\s*'([^']*)'", r.text)
if (match):
nonce = match.group(1)
else:
nonce = None
DEBUG("nonce="+nonce)
# compute hash
sha256_usr=hashlib.sha256(bytes(username,'UTF-8')).hexdigest()
hmac_usr = hmac.new(bytes(nonce,'UTF-8'), digestmod='sha256')
hmac_usr.update(sha256_usr.encode())
sha256_pass=hashlib.sha256(bytes(password,'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();
s.headers['Authorization'] = 'Digest '+credentials
# send hash during the login
DEBUG('Authorization: Digest '+credentials)
url = args.url + "/index.html"
r = s.get(url=url)
xsrf = s.cookies['XSRF-TOKEN']
DEBUG('XSRF-TOKEN'+xsrf)
s.headers['X-XSRF-TOKEN'] = xsrf
if (args.call != None):
url = args.call
info = s.get(url=url)
print(info.text)
quit()
if (not args.minimal):
# When connected, try to go to http://192.168.1.1/webGUILang.cmd?ptinGUILanguage=FR (should reply {})
url = args.url + "/webGUILang.cmd?ptinGUILanguage=FR"
r = s.get(url=url)
if (r.text != '{}'):
print("Unable to login into the box. Try again in --verbose and see what hapens...", file=sys.stderr)
quit()
Box_PrintInfo(fmt, 'login_time', str(time.time() - T))
Box_PrintInfo(fmt, 'current_time', str(datetime.datetime.now()))
Box_PrintEndSection(fmt, 'Login')
if (args.extra is not None and 'portForwarding' in args.extra):
Box_PrintBeginSection(fmt, 'PortForwarding_v4')
# http://192.168.1.1/ss-json/fgw.security/fgw.natv4.json
info = Box_GetJSON(s, args.url + "/ss-json/fgw.security/fgw.natv4.json")
# parse the portForwarding struct which is an html table
protocol = {}
protocol['0'] = 'TCP/UDP'
protocol['1'] = 'TCP'
protocol['2'] = 'UDP'
for port in parse_table(info['portForwarding']):
Box_PrintBeginSection(fmt, port[0])
Box_PrintInfo(fmt, "ip", port[6])
Box_PrintInfo(fmt, "protocol", protocol[port[3]])
Box_PrintInfo(fmt, "external", str(port[1]) + '-'+str(port[2]))
Box_PrintInfo(fmt, "internal", str(port[1]) + '-'+str(port[2]))
Box_PrintInfo(fmt, "activated", str(port[10]))
Box_PrintEndSection(fmt, port[0])
Box_PrintEndSection(fmt, 'PortForwarding_v4')
Box_PrintBeginSection(fmt, 'PortForwarding_v6')
info = Box_GetJSON(s, args.url + "/ss-json/fgw.security/fgw.natv6.json")
for port in parse_table(info['portForwarding']):
Box_PrintBeginSection(fmt, port[0])
Box_PrintInfo(fmt, "ip", port[6])
Box_PrintInfo(fmt, "protocol", protocol[port[3]])
Box_PrintInfo(fmt, "external", str(port[1]) + '-'+str(port[2]))
Box_PrintInfo(fmt, "internal", str(port[1]) + '-'+str(port[2]))
Box_PrintInfo(fmt, "activated", str(port[10]))
Box_PrintEndSection(fmt, port[0])
Box_PrintEndSection(fmt, 'PortForwarding_v6')
nbDeviceUp = 0
if (not args.minimal):
# http://192.168.1.1/ss-json/fgw.summary.json?bypass=0
Box_PrintBeginSection(fmt, 'BoxInfo')
info = Box_GetJSON(s, args.url + "/ss-json/fgw.summary.json?bypass=0")
Box_PrintInfo(fmt, 'routeSwVersion', info['router']['swVersion'])
Box_PrintInfo(fmt, 'routerUptime', info['router']['uptime'])
Box_PrintInfo(fmt, 'routerOpticalInterface', info['wan']['opticalInterface'])
Box_PrintInfo(fmt, 'lanPortStatus', info['lan']['portStatus'])
Box_PrintInfo(fmt, 'wifi0Ssid', info['wifi']['wifi0Ssid'])
Box_PrintInfo(fmt, 'wifi1Ssid', info['wifi']['wifi1Ssid'])
Box_PrintInfo(fmt, 'voiceStatus', info['voice']['status'])
Box_PrintInfo(fmt, 'voiceExtension', info['voice']['extension'])
Box_PrintInfo(fmt, 'tvStatus', info['tv']['status'])
Box_PrintEndSection(fmt, 'BoxInfo')
Box_PrintBeginSection(fmt, 'BoxInfoExtra')
info = Box_GetJSON(s, args.url + '/ss-json/fgw.home.json?bypass=0')
Box_PrintInfo(fmt, 'routerName', info['router']['name'])
Box_PrintInfo(fmt, 'wifi0GuestStatus', info['wifi']['statusWl0guest'])
Box_PrintInfo(fmt, 'wifi1GuestStatus', info['wifi']['statusWl1guest'])
Box_PrintEndSection(fmt, 'BoxInfoExtra')
Box_PrintBeginSection(fmt, 'WifiGuest')
info = Box_GetJSON(s, args.url + '/ss-json/fgw.wifi.json')
Box_PrintInfo(fmt, 'wifiGuestSsid', info['wl0']['guest']['ssid'])
Box_PrintEndSection(fmt, 'WifiGuest')
# http://192.168.1.1/ss-json/fgw.router.json
Box_PrintBeginSection(fmt, 'RouterDeviceInfo')
info = Box_GetJSON(s, args.url + '/ss-json/fgw.router.json')
Box_PrintInfo(fmt, 'routerName', info['deviceInfo']['name'])
Box_PrintInfo(fmt, 'routeSwVersion', info['deviceInfo']['swVersion'])
Box_PrintInfo(fmt, 'routerUptime', info['deviceInfo']['uptime'])
Box_PrintInfo(fmt, 'linkStatus', info['deviceInfo']['linkStatus'])
Box_PrintInfo(fmt, 'routerOpticalInterface', info['opticalInterface'])
Box_PrintEndSection(fmt, 'RouterDeviceInfo')
Box_PrintBeginSection(fmt, 'WANStatistics')
info = Box_GetJSON(s, args.url + '/ss-json/fgw.wan/fgw.wanstatistics.json')
Box_PrintInfo(fmt, 'wanRx', str(info['statistics'][0]['rx_b']))
Box_PrintInfo(fmt, 'wanTx', str(info['statistics'][0]['tx_b']))
bw_rx = None
bw_tx = None
if (args.bandwidth == True and last_report_time != None):
timediff = (datetime.datetime.now() - last_report_time).total_seconds()
bw_rx = int((info['statistics'][0]['rx_b'] - last_wanRx) / timediff)
bw_tx = int((info['statistics'][0]['tx_b'] - last_wanTx) / timediff)
Box_PrintInfo(fmt, 'wanRxBandwidth', str(bw_rx))
Box_PrintInfo(fmt, 'wanTxBandwidth', str(bw_tx))
Box_PrintEndSection(fmt, 'WANStatistics')
Box_PrintBeginSection(fmt, 'LANDevices')
info = Box_GetJSON(s, args.url + '/ss-json/fgw.common/fgw.common.devicesEth.json?bypass=0') # http://192.168.1.1/ss-json/fgw.common/fgw.common.devicesEth.json?bypass=0
i = 1
for d in info['devices']:
Box_PrintBeginSection(fmt, 'LanDevice'+str(i))
Box_PrintInfo(fmt, 'name', d['name'])
Box_PrintInfo(fmt, 'ipv4', d['ipv4'])
Box_PrintInfo(fmt, 'ipv6', d['ipv6'])
Box_PrintInfo(fmt, 'mac', d['mac'])
Box_PrintInfo(fmt, 'intf', d['intf'])
Box_PrintInfo(fmt, 'deviceStatus', str(d['status']))
if (d['status'] == 1):
nbDeviceUp = nbDeviceUp + 1
Box_PrintEndSection(fmt, 'LanDevice'+str(i))
i = i + 1
Box_PrintEndSection(fmt, 'LANDevices')
# http://192.168.1.1/ss-json/fgw.lan/fgw.lanstatistics.json
if (not args.minimal):
Box_PrintBeginSection(fmt, 'LANStatistics')
info = Box_GetJSON(s, args.url + '/ss-json/fgw.lan/fgw.lanstatistics.json')
i = 1
for stat in info['stats']:
Box_PrintBeginSection(fmt, 'Interface'+str(i))
Box_PrintInfo(fmt, 'intf', stat['intf'])
Box_PrintInfo(fmt, 'rx', str(stat['rx_b']))
Box_PrintInfo(fmt, 'tx', str(stat['tx_b']))
Box_PrintEndSection(fmt, 'Interface'+str(i))
i = i + 1
Box_PrintEndSection(fmt, 'LANStatistics')
# http://192.168.1.1/ss-json/fgw.common/fgw.common.devicesWifi.json?bypass=0
Box_PrintBeginSection(fmt, 'WifiDevices')
info = Box_GetJSON(s, args.url + '/ss-json/fgw.common/fgw.common.devicesWifi.json')
i = 1
for d in info['devices']:
Box_PrintBeginSection(fmt, 'WifiDevice'+str(i))
Box_PrintInfo(fmt, 'name', d['name'])
Box_PrintInfo(fmt, 'ipv4', d['ipv4'])
Box_PrintInfo(fmt, 'ipv6', d['ipv6'])
Box_PrintInfo(fmt, 'mac', d['mac'])
Box_PrintInfo(fmt, 'intf', d['intf'])
Box_PrintInfo(fmt, 'deviceStatus', str(d['status']))
Box_PrintEndSection(fmt, 'WifiDevice'+str(i))
if (d['status'] == 1):
nbDeviceUp = nbDeviceUp + 1
i = i + 1
Box_PrintEndSection(fmt, 'WifiDevices')
if (not args.minimal):
Box_PrintBeginSection(fmt, 'WifiStatistics')
#http://192.168.1.1/ss-json/fgw.wifi/fgw.wifistatistics.json
info = Box_GetJSON(s, args.url + '/ss-json/fgw.wifi/fgw.wifistatistics.json')
i = 1
for stat in info['stats']:
Box_PrintBeginSection(fmt, 'Interface'+str(i))
Box_PrintInfo(fmt, 'intf', stat['intf'])
Box_PrintInfo(fmt, 'rx', str(stat['rx_b']))
Box_PrintInfo(fmt, 'tx', str(stat['tx_b']))
Box_PrintEndSection(fmt, 'Interface'+str(i))
i = i + 1
Box_PrintEndSection(fmt, 'WifiStatistics')
Box_PrintBeginSection(fmt, 'OtherInformation')
Box_PrintInfo(fmt, 'nbDeviceUp', str(nbDeviceUp))
Box_PrintInfo(fmt, 'timeToComplete', str(time.time() - T))
Box_PrintEndSection(fmt, 'OtherInformation')
except requests.exceptions.Timeout as e:
print("TimeoutError! :" + str(e))
except requests.exceptions.TooManyRedirects as e:
print("Bad URL with too many redirection! :" + str(e))
except requests.exceptions.RequestException as e:
print("Request exception! :" + str(e))
except requests.exceptions.HTTPError as e:
print("HTTP error! :" + str(e))
except ValueError as e:
print(str(e))
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
Inspired by https://lafibre.info/sfr-les-news/nb6vac-et-api/
Compatible with SFR BOX FIBRE 7 (Router name : GR140CG)
Usage : python3 rbox_status.ph -u admin -p password
todo :
implement read of NAT rules : https://lafibre.info/sfr-les-news/spec-api-rest-box-de-sfr/msg1118513/#msg1118513
'''
import hashlib
import hmac
import requests
import re
import time
import argparse
import pprint
import json
import sys
#import xml.etree.ElementTree as ET
def DEBUG(text: str):
if (VERBOSE_LEVEL==True):
print(text)
def _compute_hash(token: str, value: str) -> str:
"""Compute single value hash."""
hash = hashlib.sha256(value.encode()).hexdigest()
return hmac.new(token.encode(), hash.encode(), hashlib.sha256).hexdigest()
def compute_hash(token: str, username: str, password: str) -> str:
# the original code will not work for the web interface.
# For the API, it is hash(u)+hash(p),
# but the web interface use a hash(hash(u)+hash(p))
"""Compute full username/password hash."""
hmac_username = _compute_hash(token, username)
hmac_password = _compute_hash(token, password)
return f"{hmac_username}{hmac_password}"
def Box_GetJSON(session, url):
r = session.get(url)
j = json.loads(r.text)
return j
SECTION_INC=0
def Box_PrintBeginSection(fmt, name):
global SECTION_INC
global output
f = sys.stdout if output == None else open(output, "a")
if (fmt == 'txt'):
print(">" * (3*SECTION_INC) + ">>> " + name, file=f)
SECTION_INC = SECTION_INC + 1
def Box_PrintEndSection(fmt, name):
global SECTION_INC
global output
f = sys.stdout if output == None else open(output, "a")
if (fmt == 'txt'):
print("", file=f)
SECTION_INC = SECTION_INC - 1
def Box_PrintInfo(fmt, info, value):
global output
f = sys.stdout if output == None else open(output, "a")
if (fmt == 'txt'):
print(info+": "+value.strip(), file=f)
return
def parse_table(s):
# Use regex to find all rows in the string
rows = re.findall(r'<tr[^>]*>(.*?)</tr>', s, re.DOTALL)
result = []
for row in rows:
# Find all cells in the row
cells = re.findall(r'<td[^>]*>(.*?)</td>', row, re.DOTALL)
# Clean up the cell data by stripping whitespace and HTML tags
cleaned_cells = [re.sub(r'<.*?>', '', cell).strip() for cell in cells]
result.append(cleaned_cells)
return result
def XMLAttributesToObject(string, node):
doc = ET.fromstring(string.strip())
attr = {}
for item in doc.findall(node):
for attr_name, attr_value in item.attrib.items():
attr[attr_name] = attr_value
return attr
def XMLAttributesToObjects(string, node):
doc = ET.fromstring(string.strip())
obj=[]
attr = {}
for item in doc.findall(node):
attr = {}
for attr_name, attr_value in item.attrib.items():
attr[attr_name] = attr_value
obj.append(attr)
return obj
def extractCookiesFromString(s):
# Split the string into individual key-value pairs using "; " as the separator
key_value_pairs = s.split('; ')
# Initialize an empty dictionary to hold the key-value pairs
result_dict = {}
# Process each key-value pair
for pair in key_value_pairs:
# Split on the first '=' to separate the key and value
key, value = pair.split('=', 1)
result_dict[key.strip()] = value.strip() # Add to the dictionary with stripped keys and values
return result_dict
global output
parser = argparse.ArgumentParser(
prog = 'SFR Box status',
description = 'Print advanced status of the SFR internet box.'
)
parser.add_argument('-u', '--user',
action='store',
help='username of the internet box')
parser.add_argument('-p', '--pass',
action='store',
help='password of the username')
parser.add_argument('url',
help='url of the internet box (default: %(default)s)',
default='http://192.168.1.1', nargs='?',
action='store' )
parser.add_argument('--minimal',
action='store_true',
default=False,
help='retreive minimal information')
parser.add_argument('--extra',
action='store',
type=lambda s: [str(item) for item in s.split(',')],
default=None,
help='retreive extra informations. Use a , separated list within "portForwarding"')
parser.add_argument('--call',
action='store',
default=None,
help='call directly the url. with proper authentication, you can retreive directly the json. use "http://192.168.1.1/ss-json/fgw.voice/fgw.voice.callhistory.json"')
parser.add_argument('-f', '--format',
help='output format : txt, csv, html, json (default: %(default)s)',
default='txt',
action='store')
parser.add_argument('-o', '--output',
help='output to a file',
action='store')
parser.add_argument('--token',
help='Use this token',
action='store', nargs='?')
parser.add_argument('--verbose',
action='store_true',
help='display all transaction and internal computation')
args = parser.parse_args()
password = getattr(args, 'pass') # args.pass trigger an error
username=args.user
VERBOSE_LEVEL=args.verbose
timeout = 3
DEBUG("Using box located at " + args.url)
token=args.token
fmt=args.format
output=args.output
try:
Box_PrintBeginSection(fmt, "Login")
T = time.time()
s = requests.Session() # will be use to keep cookie
# Get the nonce
url = args.url + "/uxfwk.session.loader.js"
r = s.get(url=url)
sessionid = s.cookies['SESSIONID']
DEBUG("sessionid="+sessionid)
#items=re.findall(".*nonce:.*$",r.text,re.MULTILINE)
match=re.search(r"\bnonce:\s*'([^']*)'", r.text)
if (match):
nonce = match.group(1)
else:
nonce = None
DEBUG("nonce="+nonce)
# compute hash
sha256_usr=hashlib.sha256(bytes(username,'UTF-8')).hexdigest()
hmac_usr = hmac.new(bytes(nonce,'UTF-8'), digestmod='sha256')
hmac_usr.update(sha256_usr.encode())
sha256_pass=hashlib.sha256(bytes(password,'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();
s.headers['Authorization'] = 'Digest '+credentials
# send hash during the login
DEBUG('Authorization: Digest '+credentials)
url = args.url + "/index.html"
r = s.get(url=url)
xsrf = s.cookies['XSRF-TOKEN']
DEBUG('XSRF-TOKEN'+xsrf)
s.headers['X-XSRF-TOKEN'] = xsrf
if (args.call != None):
url = args.call
info = s.get(url=url)
print(info.text)
quit()
if (not args.minimal):
# When connected, try to go to http://192.168.1.1/webGUILang.cmd?ptinGUILanguage=FR (should reply {})
url = args.url + "/webGUILang.cmd?ptinGUILanguage=FR"
r = s.get(url=url)
if (r.text != '{}'):
print("Unable to login into the box. Try again in --verbose and see what hapens...", file=sys.stderr)
quit()
Box_PrintInfo(fmt, 'login_time', str(time.time() - T))
Box_PrintEndSection(fmt, 'Login')
if (args.extra is not None and 'portForwarding' in args.extra):
Box_PrintBeginSection(fmt, 'PortForwarding_v4')
# http://192.168.1.1/ss-json/fgw.security/fgw.natv4.json
info = Box_GetJSON(s, args.url + "/ss-json/fgw.security/fgw.natv4.json")
# parse the portForwarding struct which is an html table
protocol = {}
protocol['0'] = 'TCP/UDP'
protocol['1'] = 'TCP'
protocol['2'] = 'UDP'
for port in parse_table(info['portForwarding']):
Box_PrintBeginSection(fmt, port[0])
Box_PrintInfo(fmt, "ip", port[6])
Box_PrintInfo(fmt, "protocol", protocol[port[3]])
Box_PrintInfo(fmt, "external", str(port[1]) + '-'+str(port[2]))
Box_PrintInfo(fmt, "internal", str(port[1]) + '-'+str(port[2]))
Box_PrintInfo(fmt, "activated", str(port[10]))
Box_PrintEndSection(fmt, port[0])
Box_PrintEndSection(fmt, 'PortForwarding_v4')
Box_PrintBeginSection(fmt, 'PortForwarding_v6')
info = Box_GetJSON(s, args.url + "/ss-json/fgw.security/fgw.natv6.json")
for port in parse_table(info['portForwarding']):
Box_PrintBeginSection(fmt, port[0])
Box_PrintInfo(fmt, "ip", port[6])
Box_PrintInfo(fmt, "protocol", protocol[port[3]])
Box_PrintInfo(fmt, "external", str(port[1]) + '-'+str(port[2]))
Box_PrintInfo(fmt, "internal", str(port[1]) + '-'+str(port[2]))
Box_PrintInfo(fmt, "activated", str(port[10]))
Box_PrintEndSection(fmt, port[0])
Box_PrintEndSection(fmt, 'PortForwarding_v6')
nbDeviceUp = 0
if (not args.minimal):
# http://192.168.1.1/ss-json/fgw.summary.json?bypass=0
Box_PrintBeginSection(fmt, 'BoxInfo')
info = Box_GetJSON(s, args.url + "/ss-json/fgw.summary.json?bypass=0")
Box_PrintInfo(fmt, 'routeSwVersion', info['router']['swVersion'])
Box_PrintInfo(fmt, 'routerUptime', info['router']['uptime'])
Box_PrintInfo(fmt, 'routerOpticalInterface', info['wan']['opticalInterface'])
Box_PrintInfo(fmt, 'lanPortStatus', info['lan']['portStatus'])
Box_PrintInfo(fmt, 'wifi0Ssid', info['wifi']['wifi0Ssid'])
Box_PrintInfo(fmt, 'wifi1Ssid', info['wifi']['wifi1Ssid'])
Box_PrintInfo(fmt, 'voiceStatus', info['voice']['status'])
Box_PrintInfo(fmt, 'voiceExtension', info['voice']['extension'])
Box_PrintInfo(fmt, 'tvStatus', info['tv']['status'])
Box_PrintEndSection(fmt, 'BoxInfo')
Box_PrintBeginSection(fmt, 'BoxInfoExtra')
info = Box_GetJSON(s, args.url + '/ss-json/fgw.home.json?bypass=0')
Box_PrintInfo(fmt, 'routerName', info['router']['name'])
Box_PrintInfo(fmt, 'wifi0GuestStatus', info['wifi']['statusWl0guest'])
Box_PrintInfo(fmt, 'wifi1GuestStatus', info['wifi']['statusWl1guest'])
Box_PrintEndSection(fmt, 'BoxInfoExtra')
Box_PrintBeginSection(fmt, 'WifiGuest')
info = Box_GetJSON(s, args.url + '/ss-json/fgw.wifi.json')
Box_PrintInfo(fmt, 'wifiGuestSsid', info['wl0']['guest']['ssid'])
Box_PrintEndSection(fmt, 'WifiGuest')
# http://192.168.1.1/ss-json/fgw.router.json
Box_PrintBeginSection(fmt, 'RouterDeviceInfo')
info = Box_GetJSON(s, args.url + '/ss-json/fgw.router.json')
Box_PrintInfo(fmt, 'routerName', info['deviceInfo']['name'])
Box_PrintInfo(fmt, 'routeSwVersion', info['deviceInfo']['swVersion'])
Box_PrintInfo(fmt, 'routerUptime', info['deviceInfo']['uptime'])
Box_PrintInfo(fmt, 'linkStatus', info['deviceInfo']['linkStatus'])
Box_PrintInfo(fmt, 'routerOpticalInterface', info['opticalInterface'])
Box_PrintEndSection(fmt, 'RouterDeviceInfo')
Box_PrintBeginSection(fmt, 'WANStatistics')
info = Box_GetJSON(s, args.url + '/ss-json/fgw.wan/fgw.wanstatistics.json')
Box_PrintInfo(fmt, 'wanRx', str(info['statistics'][0]['rx_b']))
Box_PrintInfo(fmt, 'wanTx', str(info['statistics'][0]['tx_b']))
Box_PrintEndSection(fmt, 'WANStatistics')
Box_PrintBeginSection(fmt, 'LANDevices')
info = Box_GetJSON(s, args.url + '/ss-json/fgw.common/fgw.common.devicesEth.json?bypass=0') # http://192.168.1.1/ss-json/fgw.common/fgw.common.devicesEth.json?bypass=0
i = 1
for d in info['devices']:
Box_PrintBeginSection(fmt, 'LanDevice'+str(i))
Box_PrintInfo(fmt, 'name', d['name'])
Box_PrintInfo(fmt, 'ipv4', d['ipv4'])
Box_PrintInfo(fmt, 'ipv6', d['ipv6'])
Box_PrintInfo(fmt, 'mac', d['mac'])
Box_PrintInfo(fmt, 'intf', d['intf'])
Box_PrintInfo(fmt, 'deviceStatus', str(d['status']))
if (d['status'] == 1):
nbDeviceUp = nbDeviceUp + 1
Box_PrintEndSection(fmt, 'LanDevice'+str(i))
i = i + 1
Box_PrintEndSection(fmt, 'LANDevices')
# http://192.168.1.1/ss-json/fgw.lan/fgw.lanstatistics.json
if (not args.minimal):
Box_PrintBeginSection(fmt, 'LANStatistics')
info = Box_GetJSON(s, args.url + '/ss-json/fgw.lan/fgw.lanstatistics.json')
i = 1
for stat in info['stats']:
Box_PrintBeginSection(fmt, 'Interface'+str(i))
Box_PrintInfo(fmt, 'intf', stat['intf'])
Box_PrintInfo(fmt, 'rx', str(stat['rx_b']))
Box_PrintInfo(fmt, 'tx', str(stat['tx_b']))
Box_PrintEndSection(fmt, 'Interface'+str(i))
i = i + 1
Box_PrintEndSection(fmt, 'LANStatistics')
# http://192.168.1.1/ss-json/fgw.common/fgw.common.devicesWifi.json?bypass=0
Box_PrintBeginSection(fmt, 'WifiDevices')
info = Box_GetJSON(s, args.url + '/ss-json/fgw.common/fgw.common.devicesWifi.json')
i = 1
for d in info['devices']:
Box_PrintBeginSection(fmt, 'WifiDevice'+str(i))
Box_PrintInfo(fmt, 'name', d['name'])
Box_PrintInfo(fmt, 'ipv4', d['ipv4'])
Box_PrintInfo(fmt, 'ipv6', d['ipv6'])
Box_PrintInfo(fmt, 'mac', d['mac'])
Box_PrintInfo(fmt, 'intf', d['intf'])
Box_PrintInfo(fmt, 'deviceStatus', str(d['status']))
Box_PrintEndSection(fmt, 'WifiDevice'+str(i))
if (d['status'] == 1):
nbDeviceUp = nbDeviceUp + 1
i = i + 1
Box_PrintEndSection(fmt, 'WifiDevices')
if (not args.minimal):
Box_PrintBeginSection(fmt, 'WifiStatistics')
#http://192.168.1.1/ss-json/fgw.wifi/fgw.wifistatistics.json
info = Box_GetJSON(s, args.url + '/ss-json/fgw.wifi/fgw.wifistatistics.json')
i = 1
for stat in info['stats']:
Box_PrintBeginSection(fmt, 'Interface'+str(i))
Box_PrintInfo(fmt, 'intf', stat['intf'])
Box_PrintInfo(fmt, 'rx', str(stat['rx_b']))
Box_PrintInfo(fmt, 'tx', str(stat['tx_b']))
Box_PrintEndSection(fmt, 'Interface'+str(i))
i = i + 1
Box_PrintEndSection(fmt, 'WifiStatistics')
Box_PrintBeginSection(fmt, 'OtherInformation')
Box_PrintInfo(fmt, 'nbDeviceUp', str(nbDeviceUp))
Box_PrintInfo(fmt, 'timeToComplete', str(time.time() - T))
Box_PrintEndSection(fmt, 'OtherInformation')
except requests.exceptions.Timeout as e:
print("TimeoutError! :" + str(e))
except requests.exceptions.TooManyRedirects as e:
print("Bad URL with too many redirection! :" + str(e))
except requests.exceptions.RequestException as e:
print("Request exception! :" + str(e))
except requests.exceptions.HTTPError as e:
print("HTTP error! :" + str(e))
except ValueError as e:
print(str(e))
@psa-jforestier
Copy link
Author

psa-jforestier commented Jun 9, 2025

Exemple :

$ python3 rbox_status2.py -u admin -p password
>>> Login
login_time: 5.49749755859375

>>> BoxInfo
routeSwVersion: 3ENT010201R00C
routerUptime: 2d 6h 27m 27s
routerOpticalInterface: Up|-26.0dBm|4.4dBm|NOS
lanPortStatus: 1|1000|Full|1|100|Full|0|0|---|0|0|---
wifi0Ssid: XXX
wifi1Ssid: XXX
voiceStatus: Up
voiceExtension: +331XXXXXXXX
tvStatus: 1

>>> BoxInfoExtra
routerName: GR140CG
wifi0GuestStatus: 1
wifi1GuestStatus: 1

>>> WifiGuest
wifiGuestSsid: jejebox_guest

>>> RouterDeviceInfo
routerName: GR140CG
routeSwVersion: 3ENT010201R00C
routerUptime: 2d 6h 27m 37s
linkStatus: 5
routerOpticalInterface: Up|-26.0dBm|4.3dBm|NOS

>>> WANStatistics
wanRx: 1188794954
wanTx: 569177370

>>> LANDevices
>>>>>> LanDevice1
name: JXXX
ipv4: 192.168.1.78
ipv6:
mac: B8:XXX
intf: eth0.0
deviceStatus: 1

>>> LANStatistics
>>>>>> Interface1
intf: eth0
rx: 25086797
tx: 204887641

>>>>>> Interface2
intf: eth1
rx: 21255638
tx: 70557527

>>>>>> Interface3
intf: eth2
rx: 0
tx: 0

>>>>>> Interface4
intf: eth3
rx: 0
tx: 0


>>> WifiDevices
>>>>>> WifiDevice1
name: JXXXX
ipv4: 192.168.1.103
ipv6: XXXXX
mac: 50:XXXX
intf: wl0
deviceStatus: 1

>>>>>> WifiDevice2
name: Google-Home-Mini
ipv4: 192.168.1.48
ipv6: 2a02:XXXX
mac: 44:XXXX
intf: wl0
deviceStatus: 1
(...)

>>> WifiStatistics
>>>>>> Interface1
intf: wl0[WiFi 2.4GHz]
rx: 860785135
tx: 2607918849

>>>>>> Interface2
intf: wl1[WiFi 5GHz]
rx: 4248568195
tx: 612480837


>>> OtherInformation
nbDeviceUp: 10
timeToComplete: 28.67517924308777

@psa-jforestier
Copy link
Author

psa-jforestier commented Jun 10, 2025

New cool option to view NAT rules :

$> python3 rbox_status2.py -u admin -p password--extra=portForwarding --minimal

>>> Login
login_time: 2.9362215995788574

>>> PortForwarding_v4
>>>>>> TESTJFO
ip: 192.168.1.254
protocol: TCP/UDP
external: 123-123
internal: 123-123
activated: 1

>>>>>> Test2
ip: 192.168.1.251
protocol: TCP
external: 100-101
internal: 100-101
activated: 0


>>> PortForwarding_v6

>>> RouterDeviceInfo
routerName: GR140CG
routeSwVersion: 3ENT010201R00C
routerUptime: 3d 3h 35m 56s
linkStatus: 5
routerOpticalInterface: Up|-26.0dBm|4.3dBm|NOS

(...)

@psa-jforestier
Copy link
Author

New cool option to call any API / URL of the box :

python3 rbox_status2.py -u admin -p password --call http://192.168.1.1/ss-json/fgw.voice/fgw.voice.callhistory.json
>>> Login
{
   "statusHistory":     "1",
   "data":              { "callHistory": [ { "accountNumber":"0", "statistics": [ { "date":"1749570992","type":"IN","destination":"+33663599959","duration":"0d 0h 0m 25s" }, { "date":"-3600","type":"-","destination":"-","duration":"0d 0h 0m 25s" }, { "date":"1749571044","type":"OUT","destination":"0139789290","duration":"0d 0h 0m 5s" }, { "date":"1749571162","type":"OUT","destination":"0139789290","duration":"0d 0h 0m 22s" }, { "date":"1749571451","type":"OUT","destination":"0139789290","duration":"0d 0h 0m 4s" }, { "date":"1749572234","type":"OUT","destination":"0139789290","duration":"0d 0h 0m 5s" }], "pad" : null  } ]},
"pad":null}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment