41 KiB
name, description, tags
| name | description | tags | ||||||
|---|---|---|---|---|---|---|---|---|
| omada-controller-administration | Class-level skill for TP-Link Omada Cloud Controller administration via browser automation. Covers login flow, SPA navigation quirks, iframe-based site management, VLAN configuration, ACL rules, inter-VLAN mDNS limitations, WLAN/channel optimization, device management, and firmware updates. Applies to cloud-based Omada controllers at omada.tplinkcloud.com. device management, and firmware updates. Applies to cloud-based Omada controllers at omada.tplinkcloud.com. |
|
TP-Link Omada Cloud Controller Administration
When to Use
Any task involving TP-Link Omada Cloud Controller — WLAN configuration, channel optimization, device management, firmware updates, site settings, or viewing logs/alerts.
Environment
- URL:
https://omada.tplinkcloud.com/ - Credentials: 1Password vault "Hermes", item "omada - Netzwerkverwaltung"
- Controller name: "Schön" (Cloud Based System)
- Site name: "Rohr"
- Devices (all on VLAN 1 / 172.16.1.x management subnet):
- Router: DR3650v-4G HW v1.0, FW 1.1.1, IP 172.16.1.1 (also 10.0.30.1 on VLAN 30)
- Switch: SG3428MP HW v6.20, FW 6.20.26 (update 6.20.27 avail), IP 172.16.1.2
- AP Erdgeschoss: EAP683 UR v1.0, FW 1.4.0, IP 172.16.1.3 (switch port 16)
- AP Obergeschoss: EAP683 UR v1.0, FW 1.4.0, IP 172.16.1.4 (switch port 14)
- AP Garage: EAP615-Wall v1.0, FW 1.5.4, IP 172.16.1.5 (switch port 21)
- AP Garten: EAP225-Outdoor v3.0, FW 5.2.2, IP 172.16.1.6
- Local router fallback:
http://172.16.1.1/webpages/login.html(login blocked when managed by controller — see "Known Issue" section) - Local switch UI:
http://172.16.1.2(accessible but requires unknown device-specific credentials) - Monitoring stack: CT 141 (proxmox7), Docker-based: prometheus (:9090), grafana (:3000), alertmanager, blackbox, pve-exporter, telegram-bridge (:9099). snmp_exporter runs as systemd binary (:9116) — Docker deployment fails on CT 141.
- Grafana dashboard:
network-infra(uid:network-infra) — Proxmox nodes, guests, HA state, blackbox probes. Omada metrics NOT included (no exporter available for cloud controller). - Local Omada Controller (in progress): CT 142 (
omada-controller, 10.0.30.142/24, VLAN 30, proxmox7, vm_disks storage). Will replace cloud controller to enable SNMP + API exporters. - Docker hosts in cluster: Only CT 141 (6 containers: monitoring stack) and CT 121 (1 container: portainer). All other CTs/VMs run services natively without Docker.
Login Flow
Steps
- Navigate to
https://omada.tplinkcloud.com/#/login - Wait 5-8 seconds — the SPA loads slowly, initial snapshot shows
(empty page)with 0 elements - Take snapshot to confirm login form is visible
- Type TP-Link ID into the "TP-Link ID (Partner ID)" textbox
- Type password into the "Password" textbox (nested inside a generic wrapper — use the inner textbox ref, not the outer one)
- Press Enter (do NOT click the "Sign In" link — it redirects to tp-link.com marketing site instead of submitting the form)
- Wait 5-8 seconds for redirect
- Dismiss cookie banner if present ("Alle Cookies akzeptieren")
- Dismiss any notice dialogs ("OK" or "Do not remind again")
Alternative: Console-Based Login
Instead of browser_type + browser_press, you can fill the login form via browser_console:
(async function(){
const sleep = ms => new Promise(r => setTimeout(r, ms));
const inputs = document.querySelectorAll('input');
if (inputs.length >= 2) {
inputs[0].focus();
inputs[0].value = 'dominik@familie-schoen.com';
inputs[0].dispatchEvent(new Event('input', {bubbles: true}));
const pwInputs = document.querySelectorAll('input[type="password"]');
if (pwInputs.length > 0) {
pwInputs[pwInputs.length - 1].focus();
pwInputs[pwInputs.length - 1].value = '<password>';
pwInputs[pwInputs.length - 1].dispatchEvent(new Event('input', {bubbles: true}));
}
}
await sleep(500);
// Submit via Enter keydown event
document.dispatchEvent(new KeyboardEvent('keydown', {key: 'Enter', code: 'Enter', keyCode: 13, which: 13, bubbles: true}));
// Also click Sign In link as backup
for (const el of document.querySelectorAll('a, button')) {
if (el.textContent.trim() === 'Sign In' && el.tagName === 'A') { el.click(); break; }
}
return 'login submitted';
})()
Note: This approach dispatches React-compatible
inputevents. Thebrowser_type+Enterapproach is simpler and equally reliable for the login form.
Critical Pitfalls
- Empty page on navigate: The Omada SPA frequently returns
(empty page)withelement_count: 0right after navigation. Always wait 5-8s then re-snapshot before concluding the page failed to load. - "Sign In" link vs form submit: The "Sign In" element (
@e9in observed session) is a link, not a submit button. Clicking it navigates tohttps://www.tp-link.com/de/support/. Usebrowser_presswithEnterwhile focused in the password field to submit the form. - Password field nesting: The password input is wrapped in a generic div containing two textboxes — use the one with aria-label "Password" (the inner ref), not the outer wrapper ref.
- Multiple dismissable dialogs: After login, expect up to 3 sequential dialogs: cookie consent, regional isolation notice, and firmware/system update announcement. Dismiss each before proceeding.
- Session expiry / white screen: After extended interaction (especially after clicking buttons inside the iframe), the SPA may go completely blank/white. The iframe disappears from the DOM entirely (
document.querySelector('iframe')returns null). Recovery: re-navigate tohttps://omada.tplinkcloud.com/#/loginand repeat the full login flow. The session does NOT persist across browser navigations. browser_consolevariable re-declaration: Repeatedbrowser_consolecalls that declare variables (e.g.,const fr = document.querySelector(...)) fail withSyntaxError: Identifier 'xxx' has already been declaredbecause the execution context persists between calls. Always wrap code in an IIFE:(function(){ ... })()to avoid polluting the persistent scope.browser_clickrefs for iframe content: Refs frombrowser_snapshotfor elements inside the iframe (e.g.,@e14for "Optimize Now") sometimes fail with "Unknown ref". Fall back tobrowser_consolewith IIFE-scoped DOM queries to click iframe-internal elements directly.
Navigation: Controller → Site → WLAN
Structure
Omada Cloud Landing
└─ Dropdown: "On Premise Systems" / "Cloud Based Systems" → select "Cloud Based Systems"
└─ Controller card ("Schön") → click to enter
└─ Global View (org overview, site list)
└─ Site "Rohr" → click site name cell
└─ Site Dashboard with left nav:
├─ Management: Dashboard, Devices, Clients
├─ Monitoring: Map, Logs
├─ Configuration: Network Config, Device Config
├─ Hotspot
└─ Maintenance: Network Tools, IntelliRecover
Switching to Cloud Based Systems
The top-left dropdown defaults to "On Premise Systems". Click the combobox, then use browser_console to find and click the "Cloud Based Systems" <li> inside .ant-dropdown-menu.
Switching Back to a Site from Global View
After performing actions in Global View (e.g., checking org-wide logs), returning to a specific site requires navigating through the Dashboard site list:
- Click Dashboard in the Global View sidebar
- Find the site name in the Site List table
- Click the
span.text-link.cursor-pointerinside the site name cell (NOT the<td>itself — it has no click handler) - Verify by checking the sidebar: site-level menus (Management, Monitoring, Configuration, Hotspot, Maintenance) should appear instead of Global View menus (Dashboard, Devices, Logs, SD-WAN, Accounts, Settings)
Pitfall: Combobox Does Not List Sites The top-left combobox (showing controller name "Schön") opens a dropdown with controller/system options (e.g., "SchönEssentials") but does NOT list individual sites. It cannot be used to switch between sites. Always use the Dashboard → Site List table to navigate to a specific site.
Entering a Site
Click the site name in the Site List table. Pitfall: clicking the <td> cell itself does NOT navigate into the site — the cell has cursor: auto and no click handler. The actual clickable element is a span.text-link.cursor-pointer inside the cell. Double-clicking the cell also does not work. Use browser_console:
(function(){
const fr = document.querySelector('iframe');
if (!fr || !fr.contentDocument) return 'no iframe';
const spans = fr.contentDocument.querySelectorAll('span.text-link, .cursor-pointer');
for (const s of spans) {
if (s.textContent.trim() === 'Rohr') { s.click(); return 'clicked Rohr text-link'; }
}
return 'not found';
})()
Verification: After clicking, the sidebar should show site-level menus (Management, Monitoring, Configuration, Hotspot, Maintenance) instead of Global View menus (Dashboard, Devices, Logs, SD-WAN, Accounts, Settings). If you still see "SD-WAN" or "Accounts" in the sidebar, you're still in Global View — the click didn't work.
Iframe Navigation
Reference: See
references/navigation-css-structure.mdfor detailed CSS class hierarchy of the Omada site iframe navigation.
Once inside a site, all content is rendered inside an <iframe>. Standard browser_click refs from the outer snapshot may not work for iframe-internal elements. Use browser_console with document.querySelector('iframe').contentDocument to find and click menu items:
(function(){
const fr = document.querySelector('iframe');
if (!fr || !fr.contentDocument) return 'no iframe';
const items = fr.contentDocument.querySelectorAll('li');
for (const item of items) {
if (item.textContent.trim() === 'WLAN') {
item.click();
return 'clicked WLAN';
}
}
return 'not found';
})()
Full Menu Structure (inside site iframe)
- Configuration → expands to:
- Network Config → General Settings (Site Settings, SSH, VoIP), Network Settings (Internet, LAN, WLAN), Authentication (Portal), VPN, Security (ACL), Transmission (Routing, NAT, DHCP Reservation, Bandwidth Control, Gateway QoS), Profile (Groups, Time Range, Rate Limit, APN Profile)
- Device Config → Gateway (DNS), Switch (Switch Ports)
VLAN Configuration
VLAN Inventory (site "Rohr")
Navigate to Configuration → Network Config → Network Settings → LAN tab.
| VLAN | Name | Subnet | Purpose |
|---|---|---|---|
| 1 | CatchAll(Default) | 172.16.1.1/24 | Default/untagged |
| 10 | Infrastructure | 10.0.10.1/24 | Network infrastructure |
| 20 | Server / Hypervisors | — | PVE hosts (10.0.20.x) |
| 30 | Server / VMs & LXCs | — | Containers & VMs (10.0.30.x) |
| 40 | Clients | — | Client devices (Mac, PC, mobile) |
| 50 | IoT | 10.0.50.1/24 | IoT devices |
| 60 | DMZ | 10.0.60.1/24 | Demilitarized zone |
| 192 | 192.168.100.x | 192.168.100.x | Special purpose |
Pitfall: The VLAN list in the LAN tab only shows 4 VLANs by default (1, 10, 50, 60). VLANs 20, 30, 40, 192 are visible in the Isolation Settings view but not in the main VLAN table. They may be configured elsewhere or inherited from the switch/gateway.
VLAN Isolation Settings
Navigate to Configuration → Network Config → Network Settings → LAN → Isolation Settings button.
Two zones:
- Isolated Network — can only access internet, no inter-VLAN communication
- Interconnected Network — can communicate with each other
As of 2026-07-05: 0 isolated, all 8 VLANs are interconnected (all can reach each other). Inter-VLAN routing is handled by the DR3650 gateway.
ACL Rules (Gateway)
Navigate to Configuration → Network Config → Security → ACL.
Key rules (LAN→LAN direction, relevant for inter-VLAN access):
| # | Description | Direction | Proto | Source → Destination |
|---|---|---|---|---|
| 1 | Permit Internet to everyone | LAN→WAN | All | All VLANs → Any |
| 5 | Allow Hypervisors from Clients | LAN→LAN | All | Clients → Hypervisors |
| 6 | Allow Clients to VMs | LAN→LAN | TCP | Clients → VMs & LXCs |
| 8 | Allow Access to IoT | LAN→LAN | All | Clients/Hyper/VMs/192 → IoT |
| 9 | DENY Botnet Stuff for IoT | LAN→LAN | Deny All | IoT/CatchAll → Clients/Hyper/VMs/Infra |
| 10 | Deny Ingress to VLANs | WAN IN | Deny All | Any → Client/Hyper/Infra/IoT/VM IPs |
Important: Rule 6 allows TCP only from Clients (VLAN 40) to VMs (VLAN 30). UDP and ICMP are NOT explicitly permitted — they may be allowed by default (no deny rule for LAN→LAN between these VLANs), but if you need UDP services (e.g. mDNS, SNMP) from VLAN 40 to VLAN 30, verify with a packet capture.
mDNS / Bonjour Across VLANs
Omada does NOT provide mDNS/multicast relay or IGMP proxy functionality.
mDNS broadcasts (224.0.0.251:5353) are confined to their originating VLAN. Bonjour service discovery (AirPlay, Time Machine, printer discovery) does NOT cross VLAN boundaries.
Workarounds:
- Manual connection — clients specify the IP/path directly
(e.g.
smb://10.0.30.21/sharefor Time Machine) - External mDNS repeater — run
mdns-repeateroravahi-reflectoron a host with interfaces in both VLANs (e.g. a CT with trunk or dual NIC)
Deployed Solution: CT 127 (avahi-reflector) on proxmox6 runs
avahi-daemon in reflector mode with interfaces in VLAN 30 (10.0.30.5,
eth0), VLAN 40 (10.0.40.5, eth1), and VLAN 50 (10.0.50.5, eth2).
Configured with a directional matrix via iptables:
- 30→40 ✅ (server services visible to clients)
- 50→30 ✅ (IoT services visible to servers)
- 50→40 ✅ (IoT services visible to clients)
- 40→30, 40→50, 30→50 ❌ (blocked by iptables INPUT/OUTPUT rules)
See the proxmox-ve-administration skill, references/avahi-reflector-setup.md
for full setup details including the multi-VLAN iptables rule pattern.
The DR3650 gateway does not expose multicast forwarding or IGMP proxy settings in the Omada controller UI. This is a firmware limitation.
Navigating to VLAN/ACL Views
VLAN list: Configuration → Network Config → Network Settings → LAN tab. Shows only 4 VLANs by default (1, 10, 50, 60). VLANs 20, 30, 40, 192 are hidden — visible only in Isolation Settings.
Isolation Settings: Click the "Isolation Settings" button on the LAN tab (next to "Add"). Shows all 8 VLANs grouped into Isolated vs Interconnected. As of 2026-07-05: 0 isolated, all interconnected.
ACL Rules: Configuration → Network Config → Security → ACL. Shows Gateway ACL rules table with INDEX, ENABLED, DESCRIPTION, DIRECTION, POLICY, PROTOCOLS, SOURCE, DESTINATION, ACTION columns.
WLAN Management
SSID Overview
Navigate to Configuration → Network Settings → WLAN. Shows tabs: SSID, WLAN Optimization, Optimization History.
Observed SSIDs (site "Rohr"):
| SSID | Security | Bands | VLAN |
|---|---|---|---|
| Schön | WPA-Personal | 2.4/5/6 GHz | 40 |
| Schön - Gäste | WPA-Personal | 2.4/5/6 GHz | — |
| IoT | WPA-Personal | 2.4/5 GHz | 50 |
| 2Schoen | WPA-Personal | 2.4/5 GHz | 40 |
WLAN Optimization (Channel Optimization)
- Navigate to WLAN → "WLAN Optimization" tab
- Click "Optimize Now" link
- Optimization Mode dialog appears with two radio options:
- Partial (default) — scans and optimizes only channels needing adjustment
- Global — full scan and reassignment of all wireless configuration
- Select mode, then click "Confirm" to start
- Warning: Internet connectivity drops for several minutes during optimization. Schedule during low-traffic periods.
- Progress can be tracked under "Optimization History" tab
- "Excluded Devices List" allows excluding specific APs from optimization
Per-SSID Channel Configuration
To manually set channels (instead of auto-optimization):
- Go to WLAN → SSID tab
- Click the SSID name or the action icon in the row
- Edit the wireless network settings — channel selection is per-band (2.4 GHz, 5 GHz, 6 GHz)
- Available 5 GHz channels observed: 36, 40, 44, 48, 149, 153, 157, 161
Async Console Automation Pattern
For multi-step flows (login → dismiss dialogs → navigate to page → action), chaining individual browser_snapshot + browser_click calls is fragile and slow due to iframe ref instability and session expiry. Instead, use a single browser_console call with an async IIFE that sleeps between steps:
(async function(){
const sleep = ms => new Promise(r => setTimeout(r, ms));
// Step 1: close notice
for (const el of document.querySelectorAll('a, button')) {
if (el.textContent.trim() === 'OK') { el.click(); break; }
}
await sleep(1000);
// Step 2: click site cell
let fr = document.querySelector('iframe');
if (!fr || !fr.contentDocument) return 'no iframe';
for (const c of fr.contentDocument.querySelectorAll('td, [role="cell"]')) {
if (c.textContent.trim() === 'Rohr') { c.click(); break; }
}
await sleep(5000);
// Step 3: expand Configuration + click WLAN
fr = document.querySelector('iframe');
for (const li of fr.contentDocument.querySelectorAll('li')) {
if (li.textContent.trim() === 'Configuration' && li.classList.contains('title')) { li.click(); break; }
}
await sleep(1000);
fr = document.querySelector('iframe');
for (const li of fr.contentDocument.querySelectorAll('li')) {
if (li.textContent.trim() === 'WLAN' && li.classList.contains('navigator-li-effective')) { li.click(); break; }
}
await sleep(3000);
// ... continue with tab click, Optimize Now, mode selection, Confirm
return 'done';
})()
Why This Works Better
- Atomic: All steps run in one execution context — no risk of session expiring between tool calls
- No ref fragility: Direct DOM queries inside iframe don't depend on snapshot refs staying valid
- Sleep-based timing:
await sleep(ms)gives SPA time to render between steps - Early return on failure: Each step checks for iframe existence and returns descriptive error if missing
Triggering WLAN Optimization (Full Async Flow)
To run the entire optimization (site click → WLAN → Optimization tab → Optimize Now → Global → Confirm) in one shot:
(async function(){
const sleep = ms => new Promise(r => setTimeout(r, ms));
// ... (site click + Configuration expansion + WLAN click as above)
// Click "WLAN Optimization" tab
fr = document.querySelector('iframe');
for (const el of fr.contentDocument.querySelectorAll('span, div, a, li')) {
if (el.textContent.trim() === 'WLAN Optimization' && el.children.length <= 1) { el.click(); break; }
}
await sleep(3000);
// Click "Optimize Now"
fr = document.querySelector('iframe');
for (const el of fr.contentDocument.querySelectorAll('a, button, span')) {
if (el.textContent.trim() === 'Optimize Now' && el.children.length <= 2) { el.click(); break; }
}
await sleep(2000);
// Select "Global" radio
fr = document.querySelector('iframe');
for (const el of fr.contentDocument.querySelectorAll('label, span, div, input')) {
if (el.textContent.trim().startsWith('Global')) { el.click(); break; }
}
await sleep(1000);
// Click "Confirm"
fr = document.querySelector('iframe');
for (const el of fr.contentDocument.querySelectorAll('a, button')) {
if (el.textContent.trim() === 'Confirm') { el.click(); return '✅ Global optimization started!'; }
}
return 'Confirm not found';
})()
Reading Optimization Results
After the optimization starts, the SPA typically goes blank (session expires). To check results:
- Re-login (navigate + login flow)
- Navigate to site Rohr → Configuration → WLAN → "Optimization History" tab
- Read the iframe body text:
(async function(){
const sleep = ms => new Promise(r => setTimeout(r, ms));
// ... (navigate to Optimization History tab as above)
fr = document.querySelector('iframe');
return fr.contentDocument.body.innerText.substring(0, 3000);
})()
The history table shows: TIME, TYPE (Manual/Adaptive), DEVICES, DURATION, MODE, RESULT (score before→after), and ACTION.
Observed result (Global mode, 2026-06-29):
Jun 29, 2026 08:36:34 am 3 4m 21s Manual
86 → 88 (+2)
Score scale: 0-100. Improvements of +2-4 points per optimization run are typical. 3 of 4 APs were optimized (one may have already been on an optimal channel).
Post-optimization: The optimization generated 2 "monitor link down" alerts (see "Pitfall: Optimization Causes Monitor Link Down Alerts" below). These were batch-resolved, bringing the total to 20 resolved / 0 unresolved.
Full Optimization Flow (Confirmed Working)
The entire flow — from Global View to optimization confirmation — can be executed as a single async IIFE after login. This was confirmed end-to-end on 2026-06-29:
- Login (Enter key in password field)
- Dismiss notice dialog (OK button)
- Click site "Rohr" cell in iframe
- Expand Configuration → click WLAN
- Click "WLAN Optimization" tab
- Click "Optimize Now" link
- Select "Global" radio in the optimization mode dialog
- Click "Confirm"
Steps 3-8 can all run in one browser_console async IIFE (see "Triggering WLAN Optimization" code block above). After Confirm, the SPA goes blank — re-login to check results via Optimization History.
Session Expiry After Confirm
Clicking "Confirm" in the optimization dialog causes the SPA to go blank — the iframe disappears entirely. This is expected: the optimization disrupts connectivity (including the controller's own management plane). Recovery requires a full re-login cycle.
Device Detail Pages
Opening Device Details — Two Levels
There are TWO ways to open device details, producing different views:
1. Simple View: Click device name (span.device-name)
Click the span.device-name element in the Devices table. Opens a
compact detail panel with limited tabs.
// Correct selector for device names in the table
const spans = doc.querySelectorAll('span.device-name');
for (const s of spans) {
if (s.textContent.trim() === 'Router') { s.click(); break; }
}
Pitfall:
span.text-linkdoes NOT match device names in the table. The correct CSS class isspan.device-name(class:device-name u-text-word-break-all ellipsis). Thespan.text-linkselector works for site names in the Site List, not device names.
2. Rich View: Click "Manage Device" button
After opening the simple view, click the "Manage Device" button
(a.manage-device-button). This opens a wider drawer with full tabs:
Router (DR3650v-4G) tabs in Manage Device view:
- Overview — Upload/Download rate, Temperature, CPU%, Memory%, Channel Utilization, Link graph, Current Clients table (shows connected clients with MAC, IP, IPv6)
- Network View — Network topology visualization
- Ports — Port-level statistics (packets, dropped, errors, bytes per port)
- Logs — Device-specific log entries
- Tools — Network Check, Terminal
- Config — Device configuration with left-side menu:
- General: Name, Description, LED, Device Labels, Remember Device
- Wireless: Radio settings
- SIM Config: PIN Management, Statistics, SMS Message, SMS Settings
- Transmission: Routing, NAT, Bandwidth Control, Gateway QoS
- Network Security: ACL
- Advanced: General, DNS
- No SNMP section exists (verified 2026-07-05 — see SNMP Monitoring section)
Switch (SG3428MP) tabs in Manage Device view:
- Same 6 tabs (Overview, Network View, Ports, Logs, Tools, Config)
- Config menu differs: General, VLAN Interface, Services (Loopback Control, Spanning Tree), Static Route
- No SNMP section exists
Pitfall: The Config tab's left-side menu uses
.ant-menu-itemclass. Some items show as[skeleton-item fake-menu-item]placeholders that never load (20 skeleton items remained permanently on the router config). Scrolling the menu container does not trigger lazy loading. This appears to be a bug in the Omada SPA when certain firmware versions don't expose all expected config sections.
Known Issue: DR3650 ISP Statistics Empty
Symptom: ISP statistics page for the DR3650v-4G router is always empty in Omada.
Root Cause: The DR3650v-4G with Hardware v1.0 / Firmware 1.1.1 does not push ISP-level telemetry (WAN traffic, cellular data volume, signal strength, connection statistics) to the Omada controller. The controller can only read basic device telemetry (temperature, CPU, memory, port status). This is a hardware/firmware limitation, not a configuration issue.
Observations (2026-06-29):
- Router detail page has only "Overview" and "Ports" tabs — no ISP/Cellular/WAN section
- Active WAN ports: DSL + Port 5 (green/active) — DSL is primary WAN, not 4G/SIM
- Router reports: 75°C, CPU 24%, Memory 57%, uptime 6+ days
- Port statistics ARE available (packets, bytes, errors per port) but aggregated WAN/ISP stats are NOT
Workaround: Enable local management on the router to access its built-in web UI directly:
- In Omada: Configuration → Device Config → Gateway → Local Management → Enable
- Access router UI at
http://10.0.30.1/webpages/login.html - ISP/cellular statistics are available in the router's own web UI under WAN/Cellular sections
Pitfall: Router Local Login Blocked When Managed by Controller
When the DR3650 is adopted by an Omada controller, the local web UI at http://10.0.30.1/webpages/login.html displays: "Note: This Gateway is being managed by Controller [IP]". Login attempts with admin credentials or the Omada cloud password will fail. Local management must be explicitly enabled through the Omada controller's Device Config → Gateway → Local Management settings before local login works.
Logs & Alerts
Viewing Alerts
Navigate to site → Logs (left nav, under Monitoring). The Logs badge shows the count of unresolved alerts. Three tabs: Alerts, Events, Audit Logs.
Filter options:
- Unresolved / Resolved / All — toggle between alert states
- System / Device — filter by alert source
- Filter link — advanced filtering
Alert Types Observed
| Type | Level | Cause | Action |
|---|---|---|---|
| Device informed monitor link down | Warning | AP lost TCP connection to Controller on port 29817 | Usually transient — see pitfall below |
Resolving Alerts (Batch Resolve)
To mark multiple alerts as resolved at once:
- Navigate to site → Logs → Alerts tab (must be in site view, NOT Global View — Global View only has "Events" and "Audit Logs" tabs, no "Alerts")
- Click the "Select all" checkbox in the table header (first
ant-checkbox-inputin the table) - Click "Batch Resolved" link (class
s-button s-button-link) in the toolbar - No confirmation dialog appears — alerts are resolved immediately
- Verify: the "Unresolved" count drops to 0
// Async flow: select all + batch resolve
(async function(){
const sleep = ms => new Promise(r => setTimeout(r, ms));
const fr = document.querySelector('iframe');
if (!fr || !fr.contentDocument) return 'no iframe';
const doc = fr.contentDocument;
// Select all checkboxes
const cbs = doc.querySelectorAll('.ant-checkbox-input');
if (cbs.length > 0) cbs[0].click(); // first visible CB = select all
await sleep(1000);
// Click "Batch Resolved"
for (const l of doc.querySelectorAll('a, button, span')) {
if (l.textContent.trim() === 'Batch Resolved' && l.offsetParent !== null) {
l.click();
return 'batch resolved';
}
}
return 'Batch Resolved not found';
})()
Checking Firmware Versions & Available Updates
Navigate to site → Devices (left nav). The device table shows all 6 devices with columns: NAME, IP, STATUS, MODEL, ACTION.
- Firmware update indicator: A green upward-arrow icon in the ACTION column signals an available firmware update for that device.
- "Start Rolling Upgrade" button (top right) initiates staged firmware updates across multiple devices.
- Devices without the arrow icon are on the latest firmware.
Observed firmware status (2026-06-29):
| Device | Model | HW | Update? |
|---|---|---|---|
| Router | DR3650v-4G | v1.0 | ✅ Current |
| Core (Switch) | SG3428MP | v6.20 | ⬆️ Update available |
| AP Erdgeschoss | EAP683 UR(EU) | v1.0 | ✅ Current |
| AP Obergeschoss | EAP683 UR(EU) | v1.0 | ✅ Current |
| AP Garage | EAP615-Wall(EU) | v1.0 | ✅ Current |
| AP Garten | EAP225-Outdoor(EU) | v3.0 | ✅ Current |
Pitfall: Optimization Causes "Monitor Link Down" Alerts
Running WLAN Optimization (especially Global mode) disrupts AP connectivity for several minutes. Each affected AP generates a "Device informed monitor link down" Warning alert (TCP connection to Controller on port 29817 failed). These are expected and harmless — the APs reconnect after optimization completes. Don't treat them as real network problems. Use "Batch Resolved" (see above) to clear them.
Pitfall: Global View vs Site View — Different Tab Sets
The Global View Logs page only has "Events" and "Audit Logs" tabs. The "Alerts" tab exists ONLY in the site-level Logs page. If you see only "Events" and "Audit Logs", you're in Global View — navigate back to the site first. Similarly, the site-level sidebar has "Management", "Monitoring", "Configuration", "Hotspot", "Maintenance" sections, while Global View has "Dashboard", "Devices", "Logs", "SD-WAN", "Accounts", "Settings". Use the sidebar structure to verify which view you're in.
SNMP Monitoring
CRITICAL FINDING: SNMP Cannot Be Enabled via Omada Cloud Controller
As of 2026-07-05, the Omada Cloud Controller does NOT expose SNMP configuration for ANY device type. This was verified exhaustively:
- Router (DR3650v-4G, FW 1.1.1): Config tab has sections: General,
Wireless, SIM Config (PIN Management, Statistics, SMS Message, SMS
Settings), Transmission (Routing, NAT, Bandwidth Control, Gateway QoS),
Network Security (ACL), Advanced (General, DNS). None contain SNMP.
Some sections show
[skeleton-item fake-menu-item]placeholders that never load (20 skeleton items remain permanently). - Switch (SG3428MP, FW 6.20.26): Config tab has sections: General, VLAN Interface, Services (Loopback Control, Spanning Tree), Static Route. No SNMP.
- APs: Config tab not tested for SNMP, but given the pattern above, unlikely to have SNMP settings either.
Conclusion: SNMP configuration is only available through the devices' local web UIs, NOT through the Omada cloud controller. The cloud controller simply does not provide this capability.
Prerequisites for SNMP via Local Device UIs
To enable SNMP on the devices, you need access to each device's local web UI:
- Router (172.16.1.1): Local login is BLOCKED — shows "Note: This Gateway is being managed by Controller 99.80.193.119". Must enable Local Management via Omada controller first (Configuration → Device Config → Gateway → Local Management — if this setting exists in your controller version).
- Switch (172.16.1.2): Local web UI is accessible (no "managed by
controller" message), but requires device-specific admin credentials.
Neither
admin/adminnor the Omada cloud password work. The local credentials are NOT stored in 1Password. - APs: Each AP may have a local web UI at its 172.16.1.x address. Credentials unknown.
Omada API Exporters — Do NOT Work with Cloud Controller
Two community Prometheus exporters exist for TP-Link Omada:
charlie-haley/omada_exporter(140 stars) — Docker imagechhaley/omada_exporterRCooLeR/omada_exporter(56 stars) — Docker imageghcr.io/rcooler/omada_exporter
Both require a LOCAL Omada controller URL (OMADA_HOST=https://192.168.x.x).
They use the Omada controller's REST API (/api/v2/...), which is only
served by on-premise controllers. The cloud controller at
omada.tplinkcloud.com returns HTML (SPA) for all API paths — it does NOT
expose a REST API backend.
To use these exporters, you would need to run a local Omada Software Controller (Docker container) and migrate devices from the cloud controller to the local one.
Local Omada Software Controller Migration (In Progress)
Decision (2026-07-05): Migrate from cloud controller to a local Omada Software Controller to enable SNMP and API-based monitoring.
Rationale: The cloud controller cannot enable SNMP or serve API exporters. A local controller unlocks both paths. Devices retain cached config during controller downtime, so the circular dependency (controller needs network ↔ network managed by controller) is not real — devices boot with last-known config and continue operating.
Deployment target: CT 142 (omada-controller, 10.0.30.142/24,
VLAN 30, proxmox7, vm_disks storage, 2 cores / 4 GB RAM / 32 GB disk).
Created via pct create with Debian 12 template.
Migration steps (planned):
- Install Docker on CT 142
- Deploy Omada Software Controller container (TP-Link official image)
- Run setup wizard (admin user, site "Rohr")
- Remove devices from cloud controller
- Adopt devices on local controller (devices at 172.16.1.x)
- Deploy
omada_exporter(charlie-haley or RCooLeR) pointing athttp://10.0.30.142:8088 - Add Prometheus scrape config for omada_exporter
- Extend Grafana dashboard with Omada panels
Circular Dependency Analysis (user-raised concern):
- Cold-start sequence: Gateway boots (cached config) → Switch boots (cached config) → PVE nodes boot → HA manager starts CT 142 → Controller reconnects to devices
- Risk: only if gateway config is corrupt AND controller is down simultaneously — very unlikely
- Mitigation: CT 142 is in HA-manager (auto-restart on any node)
Alternatives for Omada Metrics Collection
Since neither SNMP nor API exporters work with the cloud controller:
- Run a local Omada Software Controller — IN PROGRESS (CT 142). Devices will be re-adopted from the local controller. Then both SNMP (via local device UIs) and API exporters will work.
- Browser automation scraping — Periodically read device metrics (CPU, memory, temperature, port stats, client counts) from the Omada cloud controller via browser automation and push to Prometheus via pushgateway. Hacky but functional.
- Accept the limitation — The existing Grafana dashboard
(
network-infra, uid:network-infra) already tracks PVE nodes, guests, HA state, and blackbox probes. Omada device metrics (CPU, memory, temp) are visible in the Omada UI but not in Prometheus.
Device Inventory (Verified 2026-07-05)
All devices are on VLAN 1 (CatchAll/Default, 172.16.1.0/24):
| Device | Model | HW/FW | IP | Notes |
|---|---|---|---|---|
| Router | DR3650v-4G | HW v1.0 / FW 1.1.1 | 172.16.1.1 | Primary gateway, VLAN routing. Also at 10.0.30.1 (VLAN 30 interface) |
| Core (Switch) | SG3428MP | HW v6.20 / FW 6.20.26 | 172.16.1.2 | PoE switch, 28 ports, firmware update available (6.20.27) |
| AP Erdgeschoss | EAP683 UR(EU) | HW v1.0 / FW 1.4.0 | 172.16.1.3 | Connected to switch port 16 |
| AP Obergeschoss | EAP683 UR(EU) | HW v1.0 / FW 1.4.0 | 172.16.1.4 | Connected to switch port 14 |
| AP Garage | EAP615-Wall(EU) | HW v1.0 / FW 1.5.4 | 172.16.1.5 | Connected to switch port 21 |
| AP Garten | EAP225-Outdoor(EU) | HW v3.0 / FW 5.2.2 | 172.16.1.6 | Outdoor AP |
Note
: The 10.0.30.1, 10.0.20.1, etc. addresses are the gateway's VLAN interfaces, not separate devices. All Omada-managed devices use 172.16.1.x (VLAN 1 / CatchAll) as their management IP. The Omada controller communicates with devices via the organization connection IP (84.39.78.100) through the cloud.
snmp_exporter Deployment Status
snmp_exporter IS deployed on CT 141 as a binary + systemd service (v0.27.0, port 9116, using the official snmp.yml with all standard MIBs). Docker-based deployment on CT 141 does not work — see the deployment section below for details. The exporter is running and ready; it just needs SNMP to be enabled on the Omada devices to start collecting.
The monitoring stack on CT 141 runs via Docker: prometheus, grafana, alertmanager, blackbox-exporter, pve-exporter, telegram-bridge. snmp_exporter is the exception — it runs as a systemd service, not Docker.
Deploying snmp_exporter in Monitoring Stack
Critical: Docker-based deployment of snmp_exporter on CT 141 FAILS. Containers get stuck in "Created" state,
docker start/docker logs/docker inspectall hang indefinitely. This affects bothdocker runanddocker compose up. The root cause is unclear (other containers like prometheus/grafana work fine), but the pattern is 100% reproducible. Use the binary + systemd approach instead (below).
Approach A (RECOMMENDED): Binary + systemd service
Works reliably on CT 141. Tested with snmp_exporter v0.27.0.
# On CT 141 (via SSH to proxmox7, pct exec 141):
curl -sL -o /tmp/snmp_exporter.tar.gz \
https://github.com/prometheus/snmp_exporter/releases/download/v0.27.0/snmp_exporter-0.27.0.linux-amd64.tar.gz
tar xzf /tmp/snmp_exporter.tar.gz -C /tmp/
# Install binary
cp /tmp/snmp_exporter-0.27.0.linux-amd64/snmp_exporter /usr/local/bin/snmp_exporter
chmod +x /usr/local/bin/snmp_exporter
# Use the official snmp.yml bundled with the release (1.4MB, 63574 lines)
# Contains all standard MIBs including if_mib, host_resources, etc.
cp /tmp/snmp_exporter-0.27.0.linux-amd64/snmp.yml /etc/snmp_exporter.yml
# Create systemd service
cat > /etc/systemd/system/snmp-exporter.service << 'EOF'
[Unit]
Description=SNMP Exporter for Prometheus
After=network.target
[Service]
ExecStart=/usr/local/bin/snmp_exporter --config.file=/etc/snmp_exporter.yml
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now snmp-exporter
# Verify: systemctl status snmp-exporter
# Test: curl -s "http://localhost:9116/snmp?target=172.16.1.1&module=if_mib&auth=public_v2"
# Metrics: curl -s http://localhost:9116/metrics | head -5
Pitfall: v0.27.0 does NOT have the /-/healthy endpoint (returns 404).
Use curl -s http://localhost:9116/metrics to verify the exporter is alive.
Approach B (NOT RECOMMENDED): Docker container
Adding snmp_exporter as a Docker container on CT 141 does not work.
docker create succeeds but docker start hangs for 30+ seconds,
then the container enters a crash-restart loop. docker logs,
docker inspect, and docker rm all hang. The only way to recover
is docker container prune -f or waiting for operations to eventually
complete after 30-60s timeouts. Both the full 2MB snmp.yml and a
minimal custom snmp.yml produce the same result. This is specific to
CT 141 — not a general snmp_exporter Docker issue.
Prometheus Scrape Configuration
Add to /opt/monitoring/prometheus/prometheus.yml on CT 141:
- job_name: snmp_omada
scrape_interval: 30s
static_configs:
- targets:
- 172.16.1.1 # Gateway (DR3650v-4G)
- 172.16.1.2 # Switch (SG3428MP)
- 172.16.1.3 # AP Erdgeschoss (EAP683 UR)
- 172.16.1.4 # AP Obergeschoss (EAP683 UR)
- 172.16.1.5 # AP Garage (EAP615-Wall)
- 172.16.1.6 # AP Garten (EAP225-Outdoor)
metrics_path: /snmp
params:
module: [if_mib]
auth: [public_v2]
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: localhost:9116 # snmp_exporter runs as systemd, not Docker
Then reload: curl -s -X POST http://localhost:9090/-/reload
Verify: curl -s http://localhost:9090/api/v1/targets | python3 -m json.tool | grep snmp
TP-Link Enterprise OIDs
TP-Link uses enterprise OID 11863 (per IANA assignment). Key MIB subtrees:
1.3.6.1.4.1.11863.1— tp-linkDevice1.3.6.1.4.1.11863.2— tp-linkCommon1.3.6.1.4.1.11863.3— tp-linkPort1.3.6.1.4.1.11863.6— tp-linkSwitch
Standard IF-MIB (1.3.6.1.2.1.2) works for interface stats on all devices.
Host Resources MIB (1.3.6.1.2.1.25) works for CPU/memory on devices that
expose it.
Best Practices
- Always wait 5-8s after navigation before snapshotting — the SPA is slow
- Use
browser_consolefor iframe-internal interactions whenbrowser_clickrefs fail - Prefer async IIFE console chains over individual tool calls for multi-step flows — fewer session-expiry failures
- Dismiss all popup dialogs before attempting navigation
- Channel optimization disrupts connectivity — confirm with user before triggering
- Check "Optimization History" to verify optimization completed successfully
- After triggering optimization, expect session to drop — re-login to check results