feat(plugins): Add Fritz!Box device scanner plugin via TR-064 protocol#1592
feat(plugins): Add Fritz!Box device scanner plugin via TR-064 protocol#1592jokob-sk merged 9 commits intonetalertx:mainfrom
Conversation
NetAlertX had no native support for discovering devices connected to Fritz!Box routers. Users relying on Fritz!Box as their primary home router had to use generic network scanning (ARP/ICMP), missing Fritz!Box-specific details like interface type (WiFi/LAN) and connection status per device. Changes: - Add plugin implementation (front/plugins/fritzbox/fritzbox.py) Queries all hosts via FritzHosts TR-064 service, normalizes MACs, maps interface types (802.11→WiFi, Ethernet→LAN), and writes results to CurrentScan via Plugin_Objects. Supports filtering to active-only devices and optional guest WiFi monitoring via a synthetic AP device with a deterministic locally-administered MAC (02:xx derived from Fritz!Box MAC via MD5). - Add plugin configuration (front/plugins/fritzbox/config.json) Defines plugin_type "device_scanner" with settings for host, port, credentials, guest WiFi reporting, and active-only filtering. Maps scan columns to CurrentScan fields (scanMac, scanLastIP, scanName, scanType). Default schedule: every 5 minutes. - Add plugin documentation (front/plugins/fritzbox/README.md) Covers TR-064 protocol basics, quick setup guide, all settings with defaults, troubleshooting for common issues (connection refused, auth failures, no devices found), and technical details. - Add fritzconnection>=1.15.1 dependency (requirements.txt) Required Python library for TR-064 communication with Fritz!Box. - Add test suite (test/plugins/test_fritzbox.py:1-298) 298 lines covering get_connected_devices (active filtering, MAC normalization, interface mapping, error resilience), check_guest_wifi_status (service detection, SSID-based guest detection, fallback behavior), and create_guest_wifi_device (deterministic MAC generation, locally-administered bit, fallback MAC, regression anchor with precomputed hash). Users can now scan Fritz!Box-connected devices natively, seeing per-device connection status and interface type directly in NetAlertX. Guest WiFi monitoring provides visibility into guest network state. The plugin defaults to HTTPS on port 49443 with active-only filtering enabled.
The Fritz!Box plugin config.json only contained English (en_us) strings
for all translatable fields. NetAlertX supports 21 languages and shows
the plugin description and all setting labels in the user's chosen
language. Without translations, every non-English user sees raw English
text for the plugin card description, setting names, and setting
explanations regardless of their language preference.
Changes:
- front/plugins/fritzbox/config.json: added 20 translations for the
top-level plugin `description` field (all 21 supported languages)
- front/plugins/fritzbox/config.json: added translations for `name` and
`description` fields in all 14 settings (RUN, RUN_SCHD, HOST, PORT,
USER, PASS, USE_TLS, REPORT_GUEST, GUEST_SERVICE, ACTIVE_ONLY, CMD,
RUN_TIMEOUT, SET_ALWAYS, SET_EMPTY)
Selectively translated by field type:
- 12 settings: 21 languages for both name and description
- HOST (name "Fritz!Box Host") and PORT (name "TR-064 Port"): name
kept as en_us only — these are language-neutral proper names and
standard identifiers; description translated in all 21 languages
Technical terms left untranslated in all languages: Fritz!Box, TR-064,
HTTPS, HTTP, WLANConfiguration, and all code identifiers referenced
in descriptions (schedule, NEWDEV, Source = USER, Source = LOCKED)
Total: 544 localized strings added across 21 languages (ar_ar, ca_ca,
cs_cz, de_de, es_es, fa_fa, fr_fr, id_id, it_it, ja_jp, nb_no, pl_pl,
pt_br, pt_pt, ru_ru, sv_sv, tr_tr, uk_ua, vi_vn, zh_cn).
Users in all supported languages now see the plugin description card and
every setting label in their own language. The existing en_us fallback
mechanism ensures forward compatibility with any future languages added
to the project.
Two independent reliability problems were identified during PR readiness review. First, FritzConnection had no explicit timeout, meaning an unreachable or slow Fritz!Box would block the plugin process indefinitely until the OS TCP timeout fired (typically 2+ minutes), making the 60s RUN_TIMEOUT in config.json ineffective. Second, hashlib.md5() called without usedforsecurity=False raises ValueError on FIPS-enforced systems (common in enterprise Docker hosts), silently breaking the guest WiFi synthetic device feature for those users. Changes: - Add timeout=10 to FritzConnection(...) call (fritzbox.py:57) The fritzconnection library accepts a timeout parameter directly in __init__; it applies per individual HTTP request to the Fritz!Box, bounding each TR-064 call including the initial connection handshake. - Add usedforsecurity=False to hashlib.md5() call (fritzbox.py:191) The MD5 hash is used only for deterministic MAC derivation (not for any security purpose), so the flag is semantically correct and lifts the FIPS restriction without changing the computed value. - Update test assertion to include timeout=10 (test_fritzbox.py:307) assert_called_once_with checks the exact call signature; the test expectation must match the updated production code. The plugin now fails fast on unreachable Fritz!Box (within 10s per request) and works correctly on FIPS-enabled hosts. Default behavior for standard deployments is unchanged.
The device information table in README.md incorrectly stated that the
Connection Status field ("Active"/"Inactive") maps to devVendor in the
devices table. In reality, watchedValue2 has no mapped_to_column entry
in config.json, meaning the value is stored only in the plugin's own
Plugins_FRITZBOX table and never promoted to the Devices table. A user
following the documentation to filter or display Connection Status via
devVendor would find no data there.
Changes:
- Correct the "Mapped To" column for Connection Status (README.md:86)
Changed from "`devVendor` (shown as vendor field)" to "Plugin table
only (not mapped to device fields)" to accurately reflect config.json
behavior.
Users now have a correct expectation: Connection Status is visible in
the Fritz!Box plugin view but not in standard device columns. No
functional code was changed.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
📝 WalkthroughWalkthroughAdds a new Fritz!Box TR-064 device-discovery plugin, README, dependency entries, and tests; the plugin queries Fritz!Box hosts, normalizes and maps device data, optionally synthesizes a Guest WiFi device, and writes plugin result objects. Changes
Sequence Diagram(s)sequenceDiagram
participant Plugin as fritzbox.py
participant TR064 as Fritz!Box TR-064
participant Hosts as FritzHosts Service
participant WLAN as WLANConfiguration Service
participant Results as Plugin Results
Plugin->>TR064: Establish FritzConnection (host, port, TLS, creds, timeout)
alt connection fails
Plugin->>Results: Write empty/failed result file
Plugin-->>Plugin: return 1
else connection succeeds
Plugin->>Hosts: Query host list
Hosts-->>Plugin: Return host entries
loop per host
Plugin->>Plugin: Extract MAC, IP, hostname, active, iface
Plugin->>Plugin: Normalize MAC, map interface type
Plugin->>Results: Add device object
end
alt guest reporting enabled
Plugin->>WLAN: Query WLANConfiguration{N} for guest status
WLAN-->>Plugin: Guest enabled? SSID
alt guest active
Plugin->>Plugin: Synthesize Guest WiFi device (derived MAC)
Plugin->>Results: Add guest device object
end
end
Plugin->>Results: Write result file
Plugin-->>Plugin: return 0
end
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@front/plugins/fritzbox/README.md`:
- Line 192: The README timeout (60s) and the code are out of sync; update the
timeout used in fritzbox.py (where timeout=10 is passed) to use 60 seconds by
default and/or read from the RUN_TIMEOUT environment variable so the code honors
the documented behavior; modify the function or constructor that sets the
timeout (the call passing timeout=10 in fritzbox.py) to use
int(os.getenv("RUN_TIMEOUT", "60")) or equivalent so the value matches the
README and remains configurable.
In `@requirements.txt`:
- Line 37: Update the invalid dependency constraint for the fritzconnection
package in requirements.txt: replace the non-existent version specifier
"fritzconnection>=1.15.1" with a valid one such as "fritzconnection>=1.9.1" (or
the intended correct version), ensuring the requirements.txt entry for the
fritzconnection package is a valid PyPI version.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 05e9afa0-40a5-4ede-9a6b-7d9ca84d46ad
📒 Files selected for processing (5)
front/plugins/fritzbox/README.mdfront/plugins/fritzbox/config.jsonfront/plugins/fritzbox/fritzbox.pyrequirements.txttest/plugins/test_fritzbox.py
jokob-sk
left a comment
There was a problem hiding this comment.
Thanks a LOT for a very solid PR - there a a few small things, but I hope nothing too difficult to fix.
|
oh, one more thing - the docs should be updated here as well: https://github.com/netalertx/NetAlertX/blob/main/docs/PLUGINS.md |
The fritzconnection imports were originally placed inside the function bodies as a defensive pattern: by catching ImportError locally, get_fritzbox_connection() and get_connected_devices() could each return None or an empty list with a user-friendly log message instead of crashing at import time. This kept the plugin runnable even when the dependency was missing. Requested by reviewer jokob-sk in PR netalertx#1592: move all imports to the top of the module, treating fritzconnection as a required dependency that is assumed to be installed via requirements.txt. Changes: - Add top-level imports for FritzConnection and FritzHosts (fritzbox.py:16-17) - Remove inline import and ImportError handler from get_fritzbox_connection() (fritzbox.py:48, 64-67) - Remove inline import and ImportError handler from get_connected_devices() (fritzbox.py:79, 133-134) Functional behavior of the plugin is unchanged.
The fritzconnection dependency was added to the top-level requirements.txt when the Fritz!Box plugin was introduced, but the install-specific files for Proxmox and Ubuntu 24 were not updated. Without the entry in these files, fresh installations via the install scripts would not install the dependency. Requested by reviewer jokob-sk in PR netalertx#1592. Changes: - Add fritzconnection>=1.15.1 to install/proxmox/requirements.txt - Add fritzconnection>=1.15.1 to install/ubuntu24/requirements.txt All three requirements files now declare the fritzconnection dependency consistently.
Requested by reviewer jokob-sk in PR netalertx#1592. Changes: - Replace generic author "NetAlertX Community" with @sebingel (README.md:204) - Update release date from January 2026 to April 2026 (README.md:205) - Remove license field from version section (README.md:206) Project license is defined at repository level and does not need to be repeated in individual plugin READMEs. - Update repository link from jokob-sk/NetAlertX to netalertx/NetAlertX (README.md:211) The project was transferred to the netalertx organisation; the canonical URL is now github.com/netalertx/NetAlertX.
The previous implementation derived the guest WiFi device MAC using a custom MD5 hash of the Fritz!Box hardware MAC, producing a locally-administered address with a 02: prefix. This was inconsistent with the project-wide convention of using string_to_fake_mac() from crypto_utils, which produces a fa:ce: prefixed address and is used by all other plugins (nmap_dev_scan, adguard_import, pihole_api_scan, etc.). A naive switch to string_to_fake_mac(host) would have introduced a stability problem: if the user reconfigures FRITZBOX_HOST from an IP address (e.g. 192.168.178.1) to a hostname (e.g. fritz.box), the fake MAC would change and the guest device would re-appear as a new unknown device in NetAlertX. The Fritz!Box hardware MAC is a stable identifier that does not change with the configured host string. Requested by reviewer jokob-sk in PR netalertx#1592. Changes: - Remove import hashlib (fritzbox.py:3) — no longer needed - Add import string_to_fake_mac from utils.crypto_utils (fritzbox.py:15) - Replace custom MD5-based MAC derivation in create_guest_wifi_device() with string_to_fake_mac(normalize_mac(fritzbox_mac)) (fritzbox.py:178) The Fritz!Box hardware MAC is fetched via TR-064 as before, but is now passed to the shared project utility instead of a custom hash. - Add host parameter to create_guest_wifi_device(fc, host) (fritzbox.py:169) Used as fallback input to string_to_fake_mac() if the hardware MAC cannot be retrieved. - Update call site in main() to pass host (fritzbox.py:224) The guest WiFi device MAC is now stable across host configuration changes and consistent with the fa:ce: prefix convention used across the project.
The FRITZBOX plugin was not listed in the central plugin registry at docs/PLUGINS.md. Requested by reviewer jokob-sk in PR netalertx#1592. Changes: - Add FRITZBOX entry to the Available Plugins table (docs/PLUGINS.md:60) Inserted alphabetically between FREEBOX and ICMP, with type 🔍 (device_scanner) and a link to the plugin directory.
|
Done in |
|
Thanks a lot! Merging |
|
Hi @sebingel - can you please re-test the netalertx-dev image? The tests were failing so I had to make a few adjustments, but unsure if I introduced regression bugs: cc507f2#diff-0104eb1afee5afc393a0000ba35682997ab948b2f8d396097bf7eae0affc166d |
|
@jokob-sk I am so sorry that I forgot to execute the tests after this change 🤦🏻 |
|
All good -tahnsk for checking! :) |
📌 Description
Adds a new scanner plugin that discovers devices connected to a Fritz!Box router using the TR-064 protocol (UPnP-based device management API). The plugin reports MAC address, IP address, hostname, and interface type (WiFi / LAN) for each device. An optional guest WiFi monitoring feature creates a synthetic Access Point device when the guest network is active.
Full i18n coverage across 21 languages and 32 automated tests are included.
🔍 Related Issues
N/A
📋 Type of Change
Please check the relevant option(s):
📷 Screenshots or Logs (if applicable)
Example log output (verbose mode):
🧪 Testing Steps
fritz.box), port (49443), username, passwordscheduleand verify devices appear on the Devices page/tmp/log/plugins/script.FRITZBOX.logfor successful queriesdevcontainer exec --workspace-folder . pytest -q test/plugins/test_fritzbox.py— expect 32 passed✅ Checklist
🙋 Additional Notes
Technical implementation details:
fritzconnection>= 1.15.1 library via TR-064 (no web scraping)02:xx:xx:xx:xx:xx) derived from Fritz!Box MAC via MD5 withusedforsecurity=False(FIPS-compatible)normalize_mac()before every DB writePlugin_Objectsresult file mechanism throughout🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation
Tests
Chores