Fix device state reporting - handle both Integration and Legacy API formats - #2
Conversation
…ormats
## Issue
All network devices were incorrectly reported as "unknown" state on server
startup, despite being online. This caused device health monitoring to show:
- by_state: { "unknown": 6 }
- online_devices: 0/6
## Root Cause
Field type mismatch between UniFi API formats:
- Integration API returns: state = "ONLINE" (string)
- Legacy API returns: state = 1 (integer)
- Code only handled integer values: {1: "online", 0: "offline", -1: "error"}
- Result: String values fell through to "unknown" default
## Investigation Method
1. Verified devices were actually online via direct Integration API call
2. Inspected raw API response showing state = "ONLINE" (string)
3. Traced code through get_device_health_summary() and found integer-only mapping
4. Identified 3 locations with same issue across status functions
## Fix Applied
Updated device state handling in 3 locations to support both API formats:
1. get_device_health_summary() (lines 596-622)
- Normalize state to lowercase string for consistent handling
- Check isinstance() for both string and integer
- Handle both "ONLINE"/"OFFLINE" (Integration) and 1/0 (Legacy)
- Improved device type detection using model field as fallback
2. _collect_all_status() (lines 487-489)
- Updated online device counting logic
- Handles both string "ONLINE" and integer 1
3. get_quick_status() (lines 916-918)
- Fixed device online counting
- Works with both API response formats
## Verification
Before: { "by_state": { "unknown": 6 }, "online_devices": 0 }
After: { "by_state": { "online": 6 }, "online_devices": 6 }
All device types now correctly identified:
- UDM Pro, U7 Pro, U6+, USW Enterprise 24 PoE, USP PDU Pro, Cable Internet
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
WalkthroughThis change enhances device status collection to support both Integration API (string) and Legacy API (int) state representations. State handling, health summaries, device type resolution, offline detection, and online device counting logic have been updated to work with either format. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ 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: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
main.py(3 hunks)
🔇 Additional comments (4)
main.py (4)
486-489: LGTM! Dual-format state handling implemented correctly.The online device filtering correctly handles both Integration API (string "ONLINE") and Legacy API (integer 1) formats. The isinstance guards prevent type errors, and the case-insensitive string comparison with
.upper()ensures robustness.
613-613: Good improvement: device type fallback to model field.The fallback from
typetomodelfield improves device type detection when thetypefield is missing, as mentioned in the PR objectives.
617-620: LGTM! Offline detection handles both API formats correctly.The offline detection correctly checks the original
statevariable (not the normalizedstate_name) and handles both integer 0 and string "OFFLINE" formats with proper type guards and case-insensitive comparison.
918-921: LGTM! Consistent dual-format handling across functions.The online device counting logic is consistent with the changes in
_collect_all_status()(lines 486-489), correctly handling both API formats with the same type guards and comparison logic. This consistency ensures reliable device state reporting across the codebase.
| if isinstance(state, str): | ||
| state_name = state.lower() | ||
| elif isinstance(state, int): | ||
| state_name = {1: "online", 0: "offline", -1: "error"}.get(state, "unknown") |
There was a problem hiding this comment.
Fix syntax error: unquoted string literal.
The dictionary value error should be the string literal "error". This will cause a NameError at runtime when a device has state -1.
Apply this diff:
- state_name = {1: "online", 0: "offline", -1: error}.get(state, "unknown")
+ state_name = {1: "online", 0: "offline", -1: "error"}.get(state, "unknown")📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| state_name = {1: "online", 0: "offline", -1: "error"}.get(state, "unknown") | |
| state_name = {1: "online", 0: "offline", -1: "error"}.get(state, "unknown") |
🤖 Prompt for AI Agents
In main.py around line 606, the dict mapping uses an unquoted identifier error
for the -1 key which will raise a NameError at runtime; change the mapping to
use the string "error" (i.e., ensure the value is quoted) so the dict becomes
{1: "online", 0: "offline", -1: "error"}.
🐛 Issue Description
All network devices were incorrectly reported as "unknown" state on MCP server startup, despite being online and functioning normally. This affected all device health monitoring functions.
Symptoms
{ "by_state": { "unknown": 6 }, "online_devices": 0, "total_devices": 6 }Impact: Device health monitoring, status dashboards, and automation workflows received incorrect data about device states.
🔍 Root Cause Analysis
The Problem
Field type mismatch between UniFi's Integration API and Legacy API response formats:
"state": "ONLINE""state": 1Code Issue
The device state mapping only handled integer values:
Result: When Integration API returned
"ONLINE"(string), the.get()fell through to default"unknown".🔬 Investigation Method
1. Verify Devices Are Actually Online
2. Inspect Raw API Response
{ "id": "...", "name": "Dream Machine Pro", "state": "ONLINE", // ← String, not integer! "model": "UDM Pro" }3. Trace Code Execution
main.py:598{1: "online", 0: "offline"}.get(state, "unknown")4. Replicate Issue
get_device_health()→ returned"unknown": 6✅ Fix Applied
Updated 3 functions to handle both API formats:
1.
get_device_health_summary()(lines 596-622)Before:
After:
Benefits:
"ONLINE"→"online"1→"online"isinstance()checksmodelfield as fallback2.
_collect_all_status()(lines 487-489)Updated online device counting logic:
3.
get_quick_status()(lines 916-918)Same fix applied to quick status function for consistency.
✅ Verification & Testing
Before Fix
{ "total_devices": 6, "by_state": { "unknown": 6 }, "by_type": { "unknown": 6 }, "issues": [], "online_devices": 0 }After Fix
{ "total_devices": 6, "by_state": { "online": 6 }, "by_type": { "UDM Pro": 1, "U7 Pro": 1, "U6+": 1, "Cable Internet": 1, "USP PDU Pro": 1, "USW Enterprise 24 PoE": 1 }, "issues": [], "online_devices": 6 }Test Cases Verified
📊 Impact Assessment
What This Fixes
Backwards Compatibility
Performance
🧪 Testing Recommendations
Manual Testing
Expected Results
📝 Related
🎯 Checklist
🤖 Generated with Claude Code
Summary by CodeRabbit