network: migrated to nmcli.qml
This commit is contained in:
parent
617e686238
commit
36a91213b1
3 changed files with 49 additions and 154 deletions
|
|
@ -20,27 +20,27 @@ ColumnLayout {
|
||||||
StyledText {
|
StyledText {
|
||||||
Layout.topMargin: Appearance.padding.normal
|
Layout.topMargin: Appearance.padding.normal
|
||||||
Layout.rightMargin: Appearance.padding.small
|
Layout.rightMargin: Appearance.padding.small
|
||||||
text: qsTr("Wifi %1").arg(Network.wifiEnabled ? "enabled" : "disabled")
|
text: qsTr("Wifi %1").arg(Nmcli.wifiEnabled ? "enabled" : "disabled")
|
||||||
font.weight: 500
|
font.weight: 500
|
||||||
}
|
}
|
||||||
|
|
||||||
Toggle {
|
Toggle {
|
||||||
label: qsTr("Enabled")
|
label: qsTr("Enabled")
|
||||||
checked: Network.wifiEnabled
|
checked: Nmcli.wifiEnabled
|
||||||
toggle.onToggled: Network.enableWifi(checked)
|
toggle.onToggled: Nmcli.enableWifi(checked)
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
Layout.topMargin: Appearance.spacing.small
|
Layout.topMargin: Appearance.spacing.small
|
||||||
Layout.rightMargin: Appearance.padding.small
|
Layout.rightMargin: Appearance.padding.small
|
||||||
text: qsTr("%1 networks available").arg(Network.networks.length)
|
text: qsTr("%1 networks available").arg(Nmcli.networks.length)
|
||||||
color: Colours.palette.m3onSurfaceVariant
|
color: Colours.palette.m3onSurfaceVariant
|
||||||
font.pointSize: Appearance.font.size.small
|
font.pointSize: Appearance.font.size.small
|
||||||
}
|
}
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: ScriptModel {
|
model: ScriptModel {
|
||||||
values: [...Network.networks].sort((a, b) => {
|
values: [...Nmcli.networks].sort((a, b) => {
|
||||||
if (a.active !== b.active)
|
if (a.active !== b.active)
|
||||||
return b.active - a.active;
|
return b.active - a.active;
|
||||||
return b.strength - a.strength;
|
return b.strength - a.strength;
|
||||||
|
|
@ -50,7 +50,7 @@ ColumnLayout {
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: networkItem
|
id: networkItem
|
||||||
|
|
||||||
required property Network.AccessPoint modelData
|
required property Nmcli.AccessPoint modelData
|
||||||
readonly property bool isConnecting: root.connectingToSsid === modelData.ssid
|
readonly property bool isConnecting: root.connectingToSsid === modelData.ssid
|
||||||
readonly property bool loading: networkItem.isConnecting
|
readonly property bool loading: networkItem.isConnecting
|
||||||
|
|
||||||
|
|
@ -111,14 +111,14 @@ ColumnLayout {
|
||||||
|
|
||||||
StateLayer {
|
StateLayer {
|
||||||
color: networkItem.modelData.active ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface
|
color: networkItem.modelData.active ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface
|
||||||
disabled: networkItem.loading || !Network.wifiEnabled
|
disabled: networkItem.loading || !Nmcli.wifiEnabled
|
||||||
|
|
||||||
function onClicked(): void {
|
function onClicked(): void {
|
||||||
if (networkItem.modelData.active) {
|
if (networkItem.modelData.active) {
|
||||||
Network.disconnectFromNetwork();
|
Nmcli.disconnectFromNetwork();
|
||||||
} else {
|
} else {
|
||||||
root.connectingToSsid = networkItem.modelData.ssid;
|
root.connectingToSsid = networkItem.modelData.ssid;
|
||||||
Network.connectToNetwork(networkItem.modelData.ssid, "", networkItem.modelData.bssid, null);
|
Nmcli.connectToNetwork(networkItem.modelData.ssid, "", networkItem.modelData.bssid, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -151,10 +151,10 @@ ColumnLayout {
|
||||||
|
|
||||||
StateLayer {
|
StateLayer {
|
||||||
color: Colours.palette.m3onPrimaryContainer
|
color: Colours.palette.m3onPrimaryContainer
|
||||||
disabled: Network.scanning || !Network.wifiEnabled
|
disabled: Nmcli.scanning || !Nmcli.wifiEnabled
|
||||||
|
|
||||||
function onClicked(): void {
|
function onClicked(): void {
|
||||||
Network.rescanWifi();
|
Nmcli.rescanWifi();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -163,7 +163,7 @@ ColumnLayout {
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: Appearance.spacing.small
|
spacing: Appearance.spacing.small
|
||||||
opacity: Network.scanning ? 0 : 1
|
opacity: Nmcli.scanning ? 0 : 1
|
||||||
|
|
||||||
MaterialIcon {
|
MaterialIcon {
|
||||||
id: scanIcon
|
id: scanIcon
|
||||||
|
|
@ -188,22 +188,21 @@ ColumnLayout {
|
||||||
strokeWidth: Appearance.padding.small / 2
|
strokeWidth: Appearance.padding.small / 2
|
||||||
bgColour: "transparent"
|
bgColour: "transparent"
|
||||||
implicitHeight: parent.implicitHeight - Appearance.padding.smaller * 2
|
implicitHeight: parent.implicitHeight - Appearance.padding.smaller * 2
|
||||||
running: Network.scanning
|
running: Nmcli.scanning
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset connecting state when network changes
|
|
||||||
Connections {
|
Connections {
|
||||||
target: Network
|
target: Nmcli
|
||||||
|
|
||||||
function onActiveChanged(): void {
|
function onActiveChanged(): void {
|
||||||
if (Network.active && root.connectingToSsid === Network.active.ssid) {
|
if (Nmcli.active && root.connectingToSsid === Nmcli.active.ssid) {
|
||||||
root.connectingToSsid = "";
|
root.connectingToSsid = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onScanningChanged(): void {
|
function onScanningChanged(): void {
|
||||||
if (!Network.scanning)
|
if (!Nmcli.scanning)
|
||||||
scanIcon.rotation = 0;
|
scanIcon.rotation = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
137
plan.md
137
plan.md
|
|
@ -1,137 +0,0 @@
|
||||||
# Nmcli.qml Feature Completion Plan
|
|
||||||
|
|
||||||
This document outlines the missing features needed in `Nmcli.qml` to replace `Network.qml` or rewrite the wireless panel in the control center.
|
|
||||||
|
|
||||||
## Current Status
|
|
||||||
|
|
||||||
`Nmcli.qml` currently has:
|
|
||||||
- ✅ Device status queries
|
|
||||||
- ✅ Wireless/Ethernet interface listing
|
|
||||||
- ✅ Interface connection status checking
|
|
||||||
- ✅ Basic wireless connection (SSID + password)
|
|
||||||
- ✅ Disconnect functionality
|
|
||||||
- ✅ Device details (basic)
|
|
||||||
- ✅ Interface up/down
|
|
||||||
- ✅ WiFi scanning
|
|
||||||
- ✅ SSID listing with signal/security (sorted)
|
|
||||||
|
|
||||||
## Missing Features
|
|
||||||
|
|
||||||
### 1. WiFi Radio Control
|
|
||||||
- [x] `enableWifi(enabled: bool)` - Turn WiFi radio on/off
|
|
||||||
- [x] `toggleWifi()` - Toggle WiFi radio state
|
|
||||||
- [x] `wifiEnabled` property - Current WiFi radio state
|
|
||||||
- [x] Monitor WiFi radio state changes
|
|
||||||
|
|
||||||
**Implementation Notes:**
|
|
||||||
- Use `nmcli radio wifi on/off`
|
|
||||||
- Monitor state with `nmcli radio wifi`
|
|
||||||
- Update `wifiEnabled` property on state changes
|
|
||||||
|
|
||||||
### 2. Network List Management
|
|
||||||
- [x] `networks` property - List of AccessPoint objects
|
|
||||||
- [x] `active` property - Currently active network
|
|
||||||
- [x] Real-time network list updates
|
|
||||||
- [x] Network grouping by SSID with signal prioritization
|
|
||||||
- [x] AccessPoint component/object with properties:
|
|
||||||
- `ssid`, `bssid`, `strength`, `frequency`, `active`, `security`, `isSecure`
|
|
||||||
|
|
||||||
**Implementation Notes:**
|
|
||||||
- Use `nmcli -g ACTIVE,SIGNAL,FREQ,SSID,BSSID,SECURITY d w`
|
|
||||||
- Parse and group networks by SSID
|
|
||||||
- Prioritize active/connected networks
|
|
||||||
- Update network list on connection changes
|
|
||||||
|
|
||||||
### 3. Connection Management - BSSID Support
|
|
||||||
- [x] BSSID support in `connectWireless()` function
|
|
||||||
- [x] Connection profile creation with BSSID (`createConnectionWithPassword`)
|
|
||||||
- [x] Handle BSSID in connection commands
|
|
||||||
|
|
||||||
**Implementation Notes:**
|
|
||||||
- Use `nmcli connection add` with `802-11-wireless.bssid` for BSSID-based connections
|
|
||||||
- Fallback to SSID-only connection if BSSID not available
|
|
||||||
- Handle existing connection profiles when BSSID is provided
|
|
||||||
|
|
||||||
### 4. Saved Connection Profile Management
|
|
||||||
- [x] `savedConnections` property - List of saved connection names
|
|
||||||
- [x] `savedConnectionSsids` property - List of saved SSIDs
|
|
||||||
- [x] `hasSavedProfile(ssid: string)` function - Check if profile exists
|
|
||||||
- [x] `forgetNetwork(ssid: string)` function - Delete connection profile
|
|
||||||
- [x] Load saved connections on startup
|
|
||||||
- [x] Update saved connections list after connection changes
|
|
||||||
|
|
||||||
**Implementation Notes:**
|
|
||||||
- Use `nmcli -t -f NAME,TYPE connection show` to list connections
|
|
||||||
- Query SSIDs for WiFi connections: `nmcli -t -f 802-11-wireless.ssid connection show <name>`
|
|
||||||
- Use `nmcli connection delete <name>` to forget networks
|
|
||||||
- Case-insensitive SSID matching
|
|
||||||
|
|
||||||
### 5. Pending Connection Tracking
|
|
||||||
- [x] `pendingConnection` property - Track connection in progress
|
|
||||||
- [x] Connection state tracking with timers
|
|
||||||
- [x] Connection success/failure detection
|
|
||||||
- [x] Automatic retry or callback on failure
|
|
||||||
|
|
||||||
**Implementation Notes:**
|
|
||||||
- Track pending connection with SSID/BSSID
|
|
||||||
- Use timers to check connection status
|
|
||||||
- Monitor network list updates to detect successful connection
|
|
||||||
- Handle connection failures and trigger callbacks
|
|
||||||
|
|
||||||
### 6. Connection Failure Handling
|
|
||||||
- [x] `connectionFailed(ssid: string)` signal
|
|
||||||
- [x] Password requirement detection from error messages
|
|
||||||
- [x] Connection retry logic
|
|
||||||
- [x] Error message parsing and reporting
|
|
||||||
|
|
||||||
**Implementation Notes:**
|
|
||||||
- Parse stderr output for password requirements
|
|
||||||
- Detect specific error patterns (e.g., "Secrets were required")
|
|
||||||
- Emit signals for UI to handle password dialogs
|
|
||||||
- Provide meaningful error messages
|
|
||||||
|
|
||||||
### 7. Password Callback Handling
|
|
||||||
- [x] `connectToNetworkWithPasswordCheck()` function
|
|
||||||
- [x] Try connection without password first (use saved password)
|
|
||||||
- [x] Callback on password requirement
|
|
||||||
- [x] Handle both secure and open networks
|
|
||||||
|
|
||||||
**Implementation Notes:**
|
|
||||||
- Attempt connection without password for secure networks
|
|
||||||
- If connection fails with password error, trigger callback
|
|
||||||
- For open networks, connect directly
|
|
||||||
- Support callback pattern for password dialogs
|
|
||||||
|
|
||||||
### 8. Device Details Parsing
|
|
||||||
- [x] Full parsing of `device show` output
|
|
||||||
- [x] `wirelessDeviceDetails` property with:
|
|
||||||
- `ipAddress`, `gateway`, `dns[]`, `subnet`, `macAddress`
|
|
||||||
- [x] `ethernetDeviceDetails` property with:
|
|
||||||
- `ipAddress`, `gateway`, `dns[]`, `subnet`, `macAddress`, `speed`
|
|
||||||
- [x] `cidrToSubnetMask()` helper function
|
|
||||||
- [x] Update device details on connection changes
|
|
||||||
|
|
||||||
**Implementation Notes:**
|
|
||||||
- Parse `nmcli device show <interface>` output
|
|
||||||
- Extract IP4.ADDRESS, IP4.GATEWAY, IP4.DNS, etc.
|
|
||||||
- Convert CIDR notation to subnet mask
|
|
||||||
- Handle both wireless and ethernet device details
|
|
||||||
|
|
||||||
### 9. Connection Status Monitoring
|
|
||||||
- [x] Automatic network list refresh on connection changes
|
|
||||||
- [x] Monitor connection state changes
|
|
||||||
- [x] Update active network property
|
|
||||||
- [x] Refresh device details on connection
|
|
||||||
|
|
||||||
**Implementation Notes:**
|
|
||||||
- Use Process stdout SplitParser to monitor changes
|
|
||||||
- Trigger network list refresh on connection events
|
|
||||||
- Update `active` property when connection changes
|
|
||||||
- Refresh device details when connected
|
|
||||||
|
|
||||||
### 10. Ethernet Device Management
|
|
||||||
- [x] `ethernetDevices` property - List of ethernet devices
|
|
||||||
- [x] `activeEthernet` property - Currently active ethernet device
|
|
||||||
- [x] `connectEthernet(connectionName, interfaceName)` function
|
|
||||||
- [x] `disconnectEthernet(connectionName)` function
|
|
||||||
- [x] Ethernet device details parsing
|
|
||||||
|
|
@ -14,6 +14,7 @@ Singleton {
|
||||||
property string activeInterface: ""
|
property string activeInterface: ""
|
||||||
property string activeConnection: ""
|
property string activeConnection: ""
|
||||||
property bool wifiEnabled: true
|
property bool wifiEnabled: true
|
||||||
|
readonly property bool scanning: rescanProc.running
|
||||||
readonly property list<AccessPoint> networks: []
|
readonly property list<AccessPoint> networks: []
|
||||||
readonly property AccessPoint active: networks.find(n => n.active) ?? null
|
readonly property AccessPoint active: networks.find(n => n.active) ?? null
|
||||||
property list<string> savedConnections: []
|
property list<string> savedConnections: []
|
||||||
|
|
@ -235,6 +236,10 @@ Singleton {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function connectToNetwork(ssid: string, password: string, bssid: string, callback: var): void {
|
||||||
|
connectWireless(ssid, password, bssid, callback);
|
||||||
|
}
|
||||||
|
|
||||||
function connectWireless(ssid: string, password: string, bssid: string, callback: var, retryCount: int): void {
|
function connectWireless(ssid: string, password: string, bssid: string, callback: var, retryCount: int): void {
|
||||||
const hasBssid = bssid !== undefined && bssid !== null && bssid.length > 0;
|
const hasBssid = bssid !== undefined && bssid !== null && bssid.length > 0;
|
||||||
const retries = retryCount !== undefined ? retryCount : 0;
|
const retries = retryCount !== undefined ? retryCount : 0;
|
||||||
|
|
@ -473,6 +478,22 @@ Singleton {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function disconnectFromNetwork(): void {
|
||||||
|
if (active && active.ssid) {
|
||||||
|
executeCommand(["connection", "down", active.ssid], (result) => {
|
||||||
|
if (result.success) {
|
||||||
|
getNetworks(() => {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
executeCommand(["device", "disconnect", "wifi"], (result) => {
|
||||||
|
if (result.success) {
|
||||||
|
getNetworks(() => {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getDeviceDetails(interfaceName: string, callback: var): void {
|
function getDeviceDetails(interfaceName: string, callback: var): void {
|
||||||
executeCommand(["device", "show", interfaceName], (result) => {
|
executeCommand(["device", "show", interfaceName], (result) => {
|
||||||
if (callback) callback(result.output);
|
if (callback) callback(result.output);
|
||||||
|
|
@ -543,6 +564,10 @@ Singleton {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function rescanWifi(): void {
|
||||||
|
rescanProc.running = true;
|
||||||
|
}
|
||||||
|
|
||||||
function enableWifi(enabled: bool, callback: var): void {
|
function enableWifi(enabled: bool, callback: var): void {
|
||||||
const cmd = enabled ? "on" : "off";
|
const cmd = enabled ? "on" : "off";
|
||||||
executeCommand(["radio", "wifi", cmd], (result) => {
|
executeCommand(["radio", "wifi", cmd], (result) => {
|
||||||
|
|
@ -1152,6 +1177,14 @@ Singleton {
|
||||||
return details;
|
return details;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: rescanProc
|
||||||
|
command: ["nmcli", "dev", "wifi", "list", "--rescan", "yes"]
|
||||||
|
onExited: {
|
||||||
|
getNetworks(() => {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: monitorProc
|
id: monitorProc
|
||||||
running: true
|
running: true
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue