-
-
Notifications
You must be signed in to change notification settings - Fork 18
Adding Modem Support
DOCSight supports multiple cable modems through a class-based driver architecture. Here's how to add support for yours.
| Modem | Driver Key | Auth Method |
|---|---|---|
| Arris CM3500B | cm3500 |
Form POST (IP-based session, HTTPS required) |
| Netgear CM3000 | cm3000 |
Direct status page, with Login.htm form fallback on newer firmware |
| AVM FRITZ!Box (Cable) | fritzbox |
SID-based session |
| Technicolor TC4400 | tc4400 |
HTTP Basic Auth |
| Unitymedia Connect Box (CH7465) | ch7465 |
Session-based |
| Vodafone Ultra Hub 7 | ultrahub7 |
AES-CCM + PBKDF2 |
| Vodafone Station (CGA/TG) | vodafone_station |
Double PBKDF2 / AES-CCM (auto-detected) |
| Arris SURFboard (S33/S34/SB8200) | surfboard |
HNAP1 HMAC-SHA256 (HTTPS required) |
| Arris Touchstone CM8200A | cm8200 |
Base64 query string (IP-based session, HTTPS required) |
| Sagemcom F@st 3896 | sagemcom |
SHA-512 session (XMO JSON-RPC) |
| Generic Router (No DOCSIS) | generic |
None (no credentials needed) |
DOCSight v2 uses a class-based driver model:
-
ModemDriver(app/drivers/base.py) -- Abstract base class defining the interface -
Driver implementations (
app/drivers/*.py) -- One file per modem, inherits fromModemDriver -
Driver registry (
app/drivers/registry.py) --DriverRegistryclass that maps driver keys to classes (built-in + module-contributed) -
Analyzer (
app/analyzer.py) -- Transforms raw driver output into standardized format
The analyzer, storage, web UI, MQTT, and all other components work with the standardized output and don't need to know which modem produced the data.
Create app/drivers/your_modem.py:
"""Your Modem driver for DOCSight."""
import logging
import requests
from .base import ModemDriver
log = logging.getLogger("docsis.driver.your_modem")
class YourModemDriver(ModemDriver):
"""Driver for Your Modem DOCSIS 3.1 cable modem."""
def __init__(self, url: str, user: str, password: str):
super().__init__(url, user, password)
self._session = requests.Session()
def login(self) -> None:
"""Authenticate with the modem. Called before each poll cycle."""
# Implement your auth flow here
# Raise RuntimeError on failure
pass
def get_docsis_data(self) -> dict:
"""Retrieve DOCSIS channel data."""
# Must return the format described below
pass
def get_device_info(self) -> dict:
"""Retrieve device model and firmware info."""
return {
"manufacturer": "Your Manufacturer",
"model": "Your Model",
"sw_version": "",
}
def get_connection_info(self) -> dict:
"""Retrieve internet connection info (speeds, type).
Return empty dict if not available (standalone modems).
"""
return {}Your driver must return a dict with channel data. There are two supported formats:
Format A: Pre-split by DOCSIS version (preferred for mixed-mode modems)
Use this when your modem has both SC-QAM (DOCSIS 3.0) and OFDM/OFDMA (DOCSIS 3.1) channels. This ensures correct SNR interpretation (MSE for 3.0, MER for 3.1) and proper upstream power thresholds.
{
"channelDs": {
"docsis30": [
# SC-QAM downstream channels
{"channelID": "1", "type": "qam_256", "frequency": "602 MHz",
"powerLevel": 3.2, "mse": -37.5, "mer": 37.5,
"latency": 0, "corrError": 150, "nonCorrError": 5},
],
"docsis31": [
# OFDM downstream channels
{"channelID": "33", "type": "ofdm", "frequency": "763 MHz",
"powerLevel": 5.1, "mse": None, "mer": 39.0,
"latency": 0, "corrError": 0, "nonCorrError": 0},
],
},
"channelUs": {
"docsis30": [
# SC-QAM upstream channels
{"channelID": "1", "type": "qam_64", "frequency": "37 MHz",
"powerLevel": 42.0, "multiplex": ""},
],
"docsis31": [
# OFDMA upstream channels
{"channelID": "5", "type": "ofdma", "frequency": "54 MHz",
"powerLevel": 38.0, "multiplex": ""},
],
},
}Format B: Flat lists with single DOCSIS version
Use this only when all channels are the same DOCSIS version (e.g., pure DOCSIS 3.0 modem). The analyzer will place all channels into a single bucket.
{
"docsis": "3.0", # or "3.1"
"downstream": [
{"channelID": "1", "type": "qam_256", "frequency": "602 MHz",
"powerLevel": 3.2, "mse": -37.5, "mer": 37.5,
"latency": 0, "corrError": 150, "nonCorrError": 5},
],
"upstream": [
{"channelID": "1", "type": "qam_64", "frequency": "37 MHz",
"powerLevel": 42.0, "multiplex": ""},
],
}Key conventions:
- Frequency as string with MHz unit (e.g.
"602 MHz") - Power as float in dBmV
- SC-QAM downstream: set both
mse(negative) andmer(positive) - OFDM downstream: set
msetoNone, onlymer - Modulation normalized:
qam_256,qam_64,ofdm,ofdma,atdma - For mixed-mode modems, always use Format A to avoid incorrect DOCSIS version classification
Built-in drivers: Add your driver to the _BUILTINS dict in app/drivers/registry.py:
_BUILTINS = {
# ... existing drivers ...
"your_modem": "app.drivers.your_modem.YourModemDriver",
}Community drivers: You don't need to edit any DOCSight source files. Instead, package your driver as a module with "contributes": {"driver": "driver.py:YourModemDriver"} in manifest.json. See Driver Modules for details.
If your modem has a known default IP or username, add defaults to toggleUsernameField() in both app/templates/setup.html and app/templates/settings.html:
} else if(modemType === 'your_modem') {
if(!urlField.value || urlField.value === 'http://192.168.178.1') {
urlField.value = 'http://YOUR.DEFAULT.IP';
}
if(!usernameField.value) {
usernameField.value = 'admin';
}
usernameField.placeholder = 'admin';
}If your driver needs additional Python packages (e.g. beautifulsoup4 for HTML parsing), add them to requirements.txt.
# Import check
python -c "from app.drivers.your_modem import YourModemDriver"
# Docker build
docker build -t docsight:test .- Timeouts: Some modems are slow (TC4400 takes ~20s). Set appropriate timeouts (default in other drivers: 10s).
- Session management: Invalidate session state on errors so the next poll cycle triggers a fresh login.
- Only locked channels: Skip channels that aren't in "Locked" state.
- Frequency units: Modems report frequencies in Hz, kHz, or MHz. Normalize to MHz in your driver.
-
Error handling: Raise
RuntimeErrorwith descriptive messages. The polling loop catches these and retries on the next cycle.
| Driver | Complexity | Good example for |
|---|---|---|
app/drivers/fritzbox.py |
Simple | Wrapping an existing module |
app/drivers/cm3500.py |
Medium | HTML scraping, HTTPS enforcement, mixed DOCSIS 3.0/3.1 |
app/drivers/tc4400.py |
Medium | HTML scraping, Basic Auth |
app/drivers/ch7465.py |
Medium | Session-based auth |
app/drivers/ultrahub7.py |
Complex | Encrypted auth, JSON API |
app/drivers/surfboard.py |
Medium | HNAP JSON API, HMAC-SHA256 auth, HTTPS enforcement |
app/drivers/vodafone_station.py |
Complex | Multi-variant auto-detection |
Since v2026-03-03, you can contribute modem support as a community module without modifying DOCSight core. This is the recommended approach for third-party drivers.
- Create a Python file with your
ModemDriversubclass (same interface as above) - Create a
manifest.jsonwith"type": "driver"and"contributes": {"driver": "yourfile.py:YourClass"} - Test locally by mounting the module directory (see Installing Community Modules)
- Share via the Community Module Registry
Module drivers take priority over built-in drivers with the same key. For security, driver modules cannot also contribute collectors or publishers.
See Driver Modules for full documentation.
| Modem | Notes |
|---|---|
| Sagemcom F@st series | Common ISP router in several EU countries |
| SNMP generic driver | For modems exposing DOCSIS MIBs via SNMP |
Want to work on one of these? Open an issue to discuss the approach first!
Home | Quick Start | Configuration | API Reference | GitHub
- Quick Start
- Installation
- Running without Docker
- Podman Quadlet
- Configuration
- Reverse Proxy
- Example Compose Stacks
- Dashboard
- Connection Monitor
- Signal Trends
- Before/After Comparison
- Channel Timeline & Compare
- Event Log
- Smart Capture
- Gaming Quality Index
- Modulation Performance
- Cable Segment Utilization
- In-App Glossary
- Speedtest Tracker
- BNetzA Breitbandmessung
- ThinkBroadband BQM
- Smokeping
- Weather
- Netzbremse (Peering)
- Home Assistant (MQTT)
- Prometheus Metrics