-
Notifications
You must be signed in to change notification settings - Fork 121
frontend: Add USB information in system-information page #3730
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
frontend: Add USB information in system-information page #3730
Conversation
Reviewer's GuideAdds a new USB tab to the System Information view that periodically fetches USB device data from the backend, groups devices by USB bus, builds a hierarchical tree per bus, and renders it with rich metadata, icons, and search filtering. Sequence diagram for periodic USB device fetching and renderingsequenceDiagram
actor User
participant SystemInformationView
participant Usb
participant back_axios
participant BackendAPI as SystemInformationBackend
User->>SystemInformationView: Open system information page
SystemInformationView->>Usb: Render when page value is usb
activate Usb
Usb->>Usb: mounted()
Usb->>Usb: fetchUsb()
Usb->>back_axios: GET /system-information/usb (timeout 10s)
activate back_axios
back_axios->>BackendAPI: Forward request
BackendAPI-->>back_axios: UsbDevicesResponse (devices[])
back_axios-->>Usb: response.data.devices
deactivate back_axios
Usb->>Usb: devices = response.data.devices
Usb->>Usb: compute buses and treeItems
Usb-->>User: Render USB buses with treeview
deactivate Usb
loop Every 5 seconds
Usb->>Usb: fetchUsb()
Usb->>back_axios: GET /system-information/usb
alt Success
back_axios-->>Usb: UsbDevicesResponse
Usb->>Usb: Update devices and treeItems
Usb-->>User: Refresh USB tree display
else Error and backend online
back_axios-->>Usb: Error
Usb->>Usb: Set error message and stop loading
Usb-->>User: Show error alert
end
end
User-->>SystemInformationView: Navigate away from USB tab
SystemInformationView-->>Usb: Destroy component
Usb->>Usb: beforeDestroy() clearInterval(timer)
Class diagram for USB system information data structures and componentclassDiagram
class UsbDevice {
+number vid
+number pid
+string | null serial_number
+string | null manufacturer
+string | null product
+string port_path
+number bus_number
+number device_address
+number device_class
+number device_subclass
+number device_protocol
+string usb_version
+string speed
+number num_configurations
}
class UsbDevicesResponse {
+UsbDevice[] devices
}
class UsbTreeNode {
+UsbDevice device
+UsbTreeNode[] children
+number depth
+number | null portNumber
}
class TreeItem {
+string id
+string name
+TreeItem[] children
+number deviceClass
+number deviceSubclass
+number deviceProtocol
+string | null manufacturer
+string | null serial
+string vidPid
+string usbVersion
+string speed
+string portPath
+number address
}
class UsbBus {
+number busNumber
+UsbDevice | null rootHub
+TreeItem[] treeItems
}
class Usb {
<<VueComponent>>
+UsbDevice[] devices
+boolean loading
+string | null error
+number timer
+string search
+UsbBus[] buses
+UsbBus[] filteredBuses
+mounted() void
+beforeDestroy() void
+fetchUsb() Promise~void~
+deviceToTreeItem(device UsbDevice) TreeItem
+buildTreeItems(devices UsbDevice[]) TreeItem[]
+getParentPath(portPath string) string
+countDevices(items TreeItem[]) number
+hasMatchingDevice(items TreeItem[]) boolean
+isSearchMatch(item TreeItem) boolean
+filterTree(item TreeItem, search string) boolean
+getDeviceIcon(item TreeItem) string
+getDeviceIconColor(item TreeItem) string
+getDeviceTypeTooltip(deviceClass number) string
+getSpeedColor(speed string | undefined) string
+getSpeedLabel(speed string | undefined) string
+getAllNodeIds(items TreeItem[]) string[]
}
Usb "*" o-- "*" UsbDevice : uses_devices
Usb "*" o-- "*" UsbBus : groups_into
UsbBus "1" o-- "1" UsbDevice : rootHub
UsbBus "1" o-- "1" TreeItem : root_tree
TreeItem "1" o-- "*" TreeItem : children
UsbTreeNode "1" o-- "*" UsbTreeNode : children
UsbTreeNode "1" --> "1" UsbDevice : wraps
UsbDevicesResponse "1" o-- "*" UsbDevice : contains
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey - I've found 2 issues, and left some high level feedback:
- The search-related helpers (
hasMatchingDevice,isSearchMatch, andfilterTree) all duplicate very similar matching logic; consider extracting a shared predicate function to reduce repetition and keep the search behavior consistent in one place. - In
fetchUsb, whenisBackendOffline(err)is true the method returns without updatingloading, which can leave the UI in a perpetual loading state; it may be worth explicitly clearingloadingin that branch or otherwise signaling the offline state to the user. - The
getAllNodeIds(bus.treeItems)call is recomputed on every render and passed directly tov-treeview'sopenprop; if the device list grows large, consider caching these IDs per bus (e.g., via a computed property) to avoid repeated deep traversal of the tree on each update.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The search-related helpers (`hasMatchingDevice`, `isSearchMatch`, and `filterTree`) all duplicate very similar matching logic; consider extracting a shared predicate function to reduce repetition and keep the search behavior consistent in one place.
- In `fetchUsb`, when `isBackendOffline(err)` is true the method returns without updating `loading`, which can leave the UI in a perpetual loading state; it may be worth explicitly clearing `loading` in that branch or otherwise signaling the offline state to the user.
- The `getAllNodeIds(bus.treeItems)` call is recomputed on every render and passed directly to `v-treeview`'s `open` prop; if the device list grows large, consider caching these IDs per bus (e.g., via a computed property) to avoid repeated deep traversal of the tree on each update.
## Individual Comments
### Comment 1
<location> `core/frontend/src/components/system-information/Usb.vue:326-335` </location>
<code_context>
+ clearInterval(this.timer)
+ },
+ methods: {
+ async fetchUsb(): Promise<void> {
+ try {
+ const response = await back_axios({
+ method: 'get',
+ url: '/system-information/usb',
+ timeout: 10000,
+ })
+ this.devices = response.data.devices
+ this.loading = false
+ this.error = null
+ } catch (err: unknown) {
+ if (isBackendOffline(err)) return
+ this.error = `Failed to fetch USB devices: ${err instanceof Error ? err.message : 'Unknown error'}`
+ this.loading = false
</code_context>
<issue_to_address>
**issue (bug_risk):** Loading state is never cleared when the backend is offline, leaving the view stuck in a spinner state.
When `isBackendOffline(err)` is true, `fetchUsb` returns without clearing `loading`, so an initial offline request leaves the UI stuck in a spinner with no message. In the offline branch, explicitly set `loading = false` and optionally surface a non-error offline/ informational state so the UI can render a stable view.
</issue_to_address>
### Comment 2
<location> `core/frontend/src/components/system-information/Usb.vue:423-432` </location>
<code_context>
+ }
+ return false
+ },
+ isSearchMatch(item: TreeItem): boolean {
+ if (!this.search) return false
+ const searchLower = this.search.toLowerCase()
+ return (
+ item.name.toLowerCase().includes(searchLower)
+ || item.manufacturer?.toLowerCase().includes(searchLower)
+ || item.serial?.toLowerCase().includes(searchLower)
+ || item.vidPid.toLowerCase().includes(searchLower)
+ )
+ },
</code_context>
<issue_to_address>
**suggestion:** Search highlighting and filtering use slightly different criteria, which can produce confusing UX.
`filterTree`/`hasMatchingDevice` search includes `item.portPath`, but `isSearchMatch` does not. As a result, an item may appear in filtered results (matched by port path) without the `search-match` highlight. Either add `portPath` to `isSearchMatch` or restrict both to the same set of fields so filtering and highlighting stay consistent.
```suggestion
isSearchMatch(item: TreeItem): boolean {
if (!this.search) return false
const searchLower = this.search.toLowerCase()
return (
item.name.toLowerCase().includes(searchLower)
|| item.manufacturer?.toLowerCase().includes(searchLower)
|| item.serial?.toLowerCase().includes(searchLower)
|| item.vidPid.toLowerCase().includes(searchLower)
|| item.portPath.toLowerCase().includes(searchLower)
)
},
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| async fetchUsb(): Promise<void> { | ||
| try { | ||
| const response = await back_axios({ | ||
| method: 'get', | ||
| url: '/system-information/usb', | ||
| timeout: 10000, | ||
| }) | ||
| this.devices = response.data.devices | ||
| this.loading = false | ||
| this.error = null |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (bug_risk): Loading state is never cleared when the backend is offline, leaving the view stuck in a spinner state.
When isBackendOffline(err) is true, fetchUsb returns without clearing loading, so an initial offline request leaves the UI stuck in a spinner with no message. In the offline branch, explicitly set loading = false and optionally surface a non-error offline/ informational state so the UI can render a stable view.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here you are:
|
c29159f to
ffd8866
Compare
Signed-off-by: Patrick José Pereira <patrickelectric@gmail.com>
ffd8866 to
708c6df
Compare
joaoantoniocardoso
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
great!



Summary by Sourcery
Add a USB section to the system information view to display connected USB devices organized by bus and hierarchy.
New Features:
Enhancements: