A CLI and TUI tool for managing D-Link DGS series switches (DGS-1100 now, DGS-1210 planned) — 56 commands, config version control, multi-switch support, and a Prometheus exporter, all driven through headless Chrome because D-Link gave us a JavaScript web UI and nothing else.
Why this exists
The DGS-1100 is a $40 managed gigabit switch that punches way above its price. 802.1Q VLANs, port trunking, IGMP snooping, STP — real managed switch features in a fanless desktop form factor. The catch: its only management interface is a web UI built entirely in JavaScript circa 2015, with no documented API, no SSH, and SNMP that's read-only for everything useful.
If you want to set up VLANs on one switch, the web UI is fine. If you want to configure a fleet of them, or version-control your switch configs, or just not mis-click a radio button and knock yourself offline, you need something scriptable.
There's nothing to reverse-engineer in the traditional sense — no firmware to unpack, no hidden REST endpoints. The switch serves a frameset with JavaScript that populates variables by fetching .js data files via XHR. VLAN membership is a string like 'U00T0TUU' where each character is a port. Configuration changes submit forms to CGI endpoints like VLANRename.cgi. It's all right there in the page source, just tedious to drive by hand.
So this tool drives headless Chrome via Selenium to do it for you. It logs in, navigates the frames, scrapes the JS data files for current state, and fills/submits forms for changes. Every write command reads back the config after applying to verify the switch actually accepted it. 56 commands covering every feature the switch exposes — VLANs, PVIDs, STP, IGMP, storm control, port security, mirroring, QoS, bandwidth limits, SNMP, traffic segmentation, voice/surveillance VLANs, and more.
What it does
Read your config in one shot instead of clicking through 6 different pages:
$ dgs-cli status
IP: 10.1.1.250 MAC: 3C-33-32-63-01-A0
Firmware: Ver 1.00.003
┌──────────────────────────────────────────────────────────────┐
│ 802.1Q VLAN Configuration │
├──────┬──────────┬───────────────────────┬───────────────────┤
│ VID │ Name │ Untagged │ Tagged │
├──────┼──────────┼───────────────────────┼───────────────────┤
│ 3 │ IOT │ eth8 │ eth2,eth3 │
│ 4 │ GUEST │ │ eth2,eth3 │
│ 10 │ HOME │ eth1,eth4,eth5,eth6,eth7 │ eth2,eth3 │
└──────┴──────────┴───────────────────────┴───────────────────┘
┌──────────────────────────────────────────┐
│ Features │
├──────────────────────┬───────────────────┤
│ STP (RSTP) │ ON │
│ Loopback Detection │ ON │
│ IGMP Snooping │ ON │
│ Storm Control │ ON │
│ DDP │ off │
└──────────────────────┴───────────────────┘
Interactive TUI with live-updating dashboard:
$ dgs-cli tui
┌─ Ports ──────────────────────┬─ Features ────────┐
│ Port Link Speed PVID TX RX │ STP (RSTP) ON │
│ eth1 UP 1000M 10 12M 3M │ Loopback Det ON │
│ eth2 UP 1000M 10 7M 17M │ IGMP Snooping ON │
│ eth3 UP 1000M 10 5M 4M │ Storm Control ON │
│ eth4 down — 10 391 164 │ DDP off │
├─ VLANs ──────────────────────┼─ Issues ──────────┤
│ 3 IOT eth8 eth2,eth3 │ SUGGESTIONS: │
│ 4 GUEST eth2,eth3 │ * VLAN 1 is empty │
│ 10 HOME eth1..7 eth2,eth3 │ │
└──────────────────────────────┴───────────────────┘
r=Refresh h=Harden s=Save q=Quit
Audit your config for 20 categories of issues — PVID mismatches, orphaned ports, incomplete trunks, disabled security features, error counters:
$ dgs-cli recommend
⚠ ISSUES FOUND:
• Both STP and loopback detection are disabled — no loop protection!
💡 SUGGESTIONS:
• VLAN 1 is empty — consider deleting it to reduce clutter
• Storm control is disabled — broadcast storms will not be throttled
Auto-fix what recommend finds:
$ dgs-cli fix
Found 4 auto-fixable item(s):
1. Enable STP (RSTP)
2. Enable loopback detection
3. Enable storm control
4. Delete empty VLAN 1
Apply all 4 fixes? [y/N] y
>>> Enable STP (RSTP)
STP enabled (RSTP) [verified]
>>> Enable loopback detection
Loopback detection enabled [verified]
...
4/4 fixes applied — run 'save' to persist
Script changes without touching a browser:
$ dgs-cli vlan-add 20 CAMERAS U:6-7,T:2-3 VLAN 20 (CAMERAS) configured: untagged=[6, 7] tagged=[2, 3] [verified]
Batch multiple changes from a file (one login, one Chrome session):
$ cat setup.txt
# Add camera VLAN
vlan-add 20 CAMERAS U:6-7,T:2-3
pvid 6 20
pvid 7 20
harden
save
$ dgs-cli batch setup.txtVersion-control your switch config by dumping to JSON and diffing later:
$ dgs-cli dump > switch-config.json $ git add switch-config.json && git commit -m "baseline config" # ... time passes, someone changes something ... $ dgs-cli diff switch-config.json Changes from switch-config.json: ~ VLAN 10 membership: UTTUUUU0 -> UTTUUU00 ~ eth7 PVID: 10 -> 1 ~ storm_control: True -> False 3 difference(s) found
Generate reproducible configs from a dump:
$ dgs-cli template switch-config.json vlan-add 3 IOT U:8,T:2,3 vlan-add 10 HOME U:1,4,5,6,7,T:2,3 pvid 1 10 ... stp on storm on 1000 save
Manage multiple switches from one command:
$ dgs-cli --ip 10.1.1.250,10.1.2.250,10.1.3.250 recommend $ dgs-cli --ip 10.1.1.250,10.1.2.250 backup-all ./backups $ dgs-cli compare 10.1.1.250 10.1.2.250
Harden in one shot — enables STP, LBD, IGMP snooping, storm control, and disables DDP:
$ dgs-cli harden STP enabled (RSTP) [verified] Loopback detection enabled [verified] IGMP snooping enabled [verified] Storm control enabled (threshold: 1000 kbps) [verified] D-Link Discovery Protocol disabled [verified]
Supported hardware
| Model | Status |
|---|---|
| DGS-1100 (08V2, 05V2, etc.) | Supported — tested on 08V2 |
| DGS-1210 | Planned |
Auto-detects firmware path within a model family, so other DGS-1100 variants (5-port, 12-port) should work without code changes — hasn't been verified on hardware yet.
Factory default access
Out of the box, the DGS-1100 lives at 10.90.90.90 on VLAN 1 with no password. To reach it, you need to be on the same subnet:
# Temporarily add an IP on the same /8 network to reach the switch sudo ip addr add 10.90.90.91/8 dev eth0 # Verify connectivity ping 10.90.90.90 # Run setup wizard to configure VLANs, IP, password, etc. dgs-cli --ip 10.90.90.90 setup # Remove the temporary IP after reconfiguring the switch sudo ip addr del 10.90.90.91/8 dev eth0
Requirements
- Python 3 with
selenium - ChromeDriver + Chromium
- Network access to the switch
textual(optional, for TUI mode)
With Nix flake (recommended):
nix run .# -- status # run directly nix develop # dev shell with all deps
With nix-shell:
nix-shell -p 'python3.withPackages(ps: with ps; [selenium])' chromedriver chromiumConfiguration
Environment variables (defaults shown):
export SWITCH_MODEL=1100 # 1100 (default) or 1210 export SWITCH_IP=10.1.1.250 export SWITCH_PASS=admin export SWITCH_AUDIT_LOG=~/.switch-audit.log # optional, enables audit logging
Or pass as flags:
dgs-cli --1100 --ip 192.168.1.1 --pass secret status dgs-cli --1210 --ip 10.1.2.1 status
Usage
dgs-cli [--1100|--1210] [--ip IP] [--pass PASS] [--json] [command]All commands
63 commands organized by category. Run help COMMAND for detailed usage.
Read (safe, no changes):
status dump diff recommend fdb info pages read cable-diag export-csv
VLANs:
vlan-add vlan-delete vlan-rename pvid pvid-all
Ports:
port (enable/disable/speed/flow/describe) mirror qos bandwidth
Security:
stp stp-port lbd igmp storm port-security ddp traffic-segm static-mac fdb-learning
Network:
ip mgmt-vlan snmp snmp-community snmp-host voice-vlan surv-vlan jumbo eee
Operations:
harden fix restore compare clone template backup-all batch watch tui prometheus webhook history
Admin:
password sysname save reboot reset setup
Port spec format
Ports are specified as comma-separated values with optional ranges:
1— single port1,3,5— multiple ports4-8— range1,4-8— mixed
VLAN port spec format
Prefix ports with U: (untagged) or T: (tagged):
U:1,4-8— ports 1,4-8 untaggedT:2-3— ports 2-3 taggedU:1,4-8,T:2-3— combined
Config analysis
recommend crawls 13 data files from the switch and checks for:
- PVID/untagged VLAN mismatches
- Ports not in any VLAN
- Ports untagged in multiple VLANs
- Trunk ports missing VLANs
- Trunk group member consistency
- Management VLAN disabled or nonexistent
- Port admin state (disabled ports flagged)
- TX/RX error counters
- Zero-traffic ports
- Flow control enabled (head-of-line blocking risk)
- Empty VLAN 1
- STP and loopback detection status
- IGMP snooping disabled with multiple VLANs
- Port mirroring active (performance impact)
- Storm control disabled
- Port security disabled on all ports
- Bandwidth limits configured
- DHCP-assigned switch IP
- D-Link Discovery Protocol enabled
Shell completions
# Bash eval "$(dgs-cli --completions bash)" # Zsh eval "$(dgs-cli --completions zsh)" # Fish dgs-cli --completions fish | source
Monitoring
Prometheus exporter — serves port stats, link state, and feature flags:
$ dgs-cli prometheus 9100 # http://localhost:9100/metrics # switch_port_tx_ok{port="eth1"} 12432179 # switch_port_link{port="eth1"} 1 # switch_feature_enabled{feature="stp"} 1
Webhook — POST issues to Slack, Mattermost, or any URL:
$ dgs-cli webhook https://hooks.slack.com/services/...
Audit log
Set SWITCH_AUDIT_LOG to log every write command with timestamp and switch IP:
export SWITCH_AUDIT_LOG=~/.switch-audit.log dgs-cli stp on dgs-cli history # 2026-03-23T19:30:00 10.1.1.250 stp on
Filter by switch or command:
dgs-cli history --ip 10.1.1.250 --cmd vlan-addTests
nix develop pytest test_dgs_cli.py -v
142 tests, all offline (mock driver). Covers parsing, VLAN data extraction, health data parsing, and config analysis logic across all 20 check categories.