Files
Dashboard/server/routes/fritzbox.ts
T
2026-05-10 21:23:42 +02:00

67 lines
2.5 KiB
TypeScript

import { Router } from 'express'
import axios from 'axios'
const router = Router()
const HISTORY_SIZE = 20
const history: { ts: number; rx: number; tx: number }[] = []
function soap(action: string, service: string, body = ''): string {
return `<?xml version="1.0" encoding="utf-8"?><s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:${action} xmlns:u="${service}">${body}</u:${action}></s:Body></s:Envelope>`
}
function tag(xml: string, name: string): string {
const m = xml.match(new RegExp(`<(?:[^:>]*:)?${name}>([^<]*)<`))
return m?.[1]?.trim() ?? ''
}
async function soapReq(host: string, path: string, service: string, action: string): Promise<string> {
const res = await axios.post(`${host}:49000${path}`, soap(action, service), {
headers: {
'Content-Type': 'text/xml; charset="utf-8"',
SOAPAction: `"${service}#${action}"`,
},
timeout: 5000,
})
return res.data as string
}
router.get('/status', async (_req, res) => {
try {
const host = process.env.FRITZBOX_HOST
if (!host) {
res.status(503).json({ error: 'FRITZBOX_HOST not configured' })
return
}
const [addonXml, ipXml, statusXml] = await Promise.all([
soapReq(host, '/igdupnp/control/WANCommonIFC1',
'urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1', 'GetAddonInfos'),
soapReq(host, '/igdupnp/control/WANIPConn1',
'urn:schemas-upnp-org:service:WANIPConnection:1', 'GetExternalIPAddress'),
soapReq(host, '/igdupnp/control/WANIPConn1',
'urn:schemas-upnp-org:service:WANIPConnection:1', 'GetStatusInfo'),
])
// Bytes/sec — FritzBox provides instantaneous rates
const rxRate = Number(tag(addonXml, 'NewByteReceiveRate') || tag(addonXml, 'NewBytesReceiveRate') || '0')
const txRate = Number(tag(addonXml, 'NewByteSendRate') || tag(addonXml, 'NewBytesSendRate') || '0')
history.push({ ts: Date.now(), rx: rxRate, tx: txRate })
if (history.length > HISTORY_SIZE) history.shift()
res.json({
connected: tag(statusXml, 'NewConnectionStatus') === 'Connected',
externalIp: tag(ipXml, 'NewExternalIPAddress'),
rxMbps: parseFloat((rxRate * 8 / 1_000_000).toFixed(2)),
txMbps: parseFloat((txRate * 8 / 1_000_000).toFixed(2)),
history: history.map(h => ({ rx: h.rx, tx: h.tx })),
})
} catch (err: unknown) {
const msg = err instanceof Error ? err.message : 'Unknown error'
res.status(500).json({ error: msg })
}
})
export default router