Merge PR #898: Add support for wired network configuration

pull/901/head
jenkins 2022-10-11 09:12:07 +02:00
commit e7475150cd
5 changed files with 325 additions and 27 deletions

View File

@ -40,6 +40,10 @@
#include <QMetaEnum>
#include <QJsonDocument>
#include "logging.h"
NYMEA_LOGGING_CATEGORY(dcNetworkManagement, "NetworkManagement")
NetworkManager::NetworkManager(QObject *parent):
QObject(parent),
m_wiredNetworkDevices(new WiredNetworkDevices(this)),
@ -167,6 +171,41 @@ int NetworkManager::startAccessPoint(const QString &interface, const QString &ss
return m_engine->jsonRpcClient()->sendCommand("NetworkManager.StartAccessPoint", params, this, "startAccessPointResponse");
}
int NetworkManager::createWiredAutoConnection(const QString &interface)
{
QVariantMap params {
{"interface", interface},
{"type", "WiredNetworkConnectionTypeDHCP"}
};
return m_engine->jsonRpcClient()->sendCommand("NetworkManager.CreateWiredConnection", params, this, "createWiredAutoConnectionResponse");
}
int NetworkManager::createWiredManualConnection(const QString &interface, const QString &ip, quint8 prefix, const QString &gateway, const QString &dns)
{
qCDebug(dcNetworkManagement()) << "Creating manual connection" << interface << ip << prefix << gateway << dns;
QVariantMap params {
{"interface", interface},
{"type", "WiredNetworkConnectionTypeManual"},
{"ip", ip},
{"prefix", prefix},
{"gateway", gateway},
{"dns", dns}
};
return m_engine->jsonRpcClient()->sendCommand("NetworkManager.CreateWiredConnection", params, this, "createWiredManualConnectionResponse");
}
int NetworkManager::createWiredSharedConnection(const QString &interface, const QString &ip, quint8 prefix)
{
qCDebug(dcNetworkManagement()) << "Creating shared connection" << interface << ip << prefix;
QVariantMap params {
{"interface", interface},
{"type", "WiredNetworkConnectionTypeShared"},
{"ip", ip},
{"prefix", prefix}
};
return m_engine->jsonRpcClient()->sendCommand("NetworkManager.CreateWiredConnection", params, this, "createWiredSharedConnectionResponse");
}
int NetworkManager::disconnectInterface(const QString &interface)
{
QVariantMap params;
@ -212,7 +251,7 @@ void NetworkManager::getStatusResponse(int /*commandId*/, const QVariantMap &par
void NetworkManager::getDevicesResponse(int /*commandId*/, const QVariantMap &params)
{
// qDebug() << "Devices reply" << commandId << qUtf8Printable(QJsonDocument::fromVariant(params).toJson(QJsonDocument::Indented));
qCDebug(dcNetworkManagement) << "Devices reply" << qUtf8Printable(QJsonDocument::fromVariant(params).toJson(QJsonDocument::Indented));
foreach (const QVariant &deviceVariant, params.value("wiredNetworkDevices").toList()) {
QVariantMap deviceMap = deviceVariant.toMap();
@ -248,7 +287,7 @@ void NetworkManager::getDevicesResponse(int /*commandId*/, const QVariantMap &pa
void NetworkManager::getAccessPointsResponse(int commandId, const QVariantMap &params)
{
qDebug() << "Access points reply" << qUtf8Printable(QJsonDocument::fromVariant(params).toJson(QJsonDocument::Indented));
qCDebug(dcNetworkManagement) << "Access points reply" << qUtf8Printable(QJsonDocument::fromVariant(params).toJson(QJsonDocument::Indented));
if (!m_apRequests.contains(commandId)) {
qWarning() << "NetworkManager received a reply for a request we don't know!";
@ -279,43 +318,87 @@ void NetworkManager::getAccessPointsResponse(int commandId, const QVariantMap &p
void NetworkManager::connectToWiFiResponse(int commandId, const QVariantMap &params)
{
qDebug() << "connect to wifi reply" << qUtf8Printable(QJsonDocument::fromVariant(params).toJson(QJsonDocument::Indented));
qCDebug(dcNetworkManagement) << "connect to wifi reply" << qUtf8Printable(QJsonDocument::fromVariant(params).toJson(QJsonDocument::Indented));
QString status = params.value("networkManagerError").toString();
emit connectToWiFiReply(commandId, status);
}
void NetworkManager::disconnectResponse(int commandId, const QVariantMap &params)
{
qDebug() << "disconnect reply" << qUtf8Printable(QJsonDocument::fromVariant(params).toJson(QJsonDocument::Indented));
qCDebug(dcNetworkManagement) << "disconnect reply" << qUtf8Printable(QJsonDocument::fromVariant(params).toJson(QJsonDocument::Indented));
QString status = params.value("networkManagerError").toString();
emit disconnectReply(commandId, status);
}
void NetworkManager::enableNetworkingResponse(int commandId, const QVariantMap &params)
{
qDebug() << "enable networking reply" << qUtf8Printable(QJsonDocument::fromVariant(params).toJson(QJsonDocument::Indented));
qCDebug(dcNetworkManagement) << "enable networking reply" << qUtf8Printable(QJsonDocument::fromVariant(params).toJson(QJsonDocument::Indented));
QString status = params.value("networkManagerError").toString();
emit enableNetworkingReply(commandId, status);
}
void NetworkManager::enableWirelessNetworkingResponse(int commandId, const QVariantMap &params)
{
qDebug() << "enable wireless networking reply" << qUtf8Printable(QJsonDocument::fromVariant(params).toJson(QJsonDocument::Indented));
qCDebug(dcNetworkManagement) << "enable wireless networking reply" << qUtf8Printable(QJsonDocument::fromVariant(params).toJson(QJsonDocument::Indented));
QString status = params.value("networkManagerError").toString();
emit enableWirelessNetworkingReply(commandId, status);
}
void NetworkManager::startAccessPointResponse(int commandId, const QVariantMap &params)
{
qDebug() << "Start access point reply" << qUtf8Printable(QJsonDocument::fromVariant(params).toJson(QJsonDocument::Indented));
qCDebug(dcNetworkManagement) << "Start access point reply" << qUtf8Printable(QJsonDocument::fromVariant(params).toJson(QJsonDocument::Indented));
QString status = params.value("networkManagerError").toString();
emit startAccessPointReply(commandId, status);
}
void NetworkManager::createWiredAutoConnectionResponse(int commandId, const QVariantMap &params)
{
QString status = params.value("networkManagerError").toString();
emit createWiredAutoConnectionReply(commandId, status);
}
void NetworkManager::createWiredManualConnectionResponse(int commandId, const QVariantMap &params)
{
QString status = params.value("networkManagerError").toString();
emit createWiredManualConnectionReply(commandId, status);
}
void NetworkManager::createWiredSharedConnectionResponse(int commandId, const QVariantMap &params)
{
QString status = params.value("networkManagerError").toString();
emit createWiredSharedConnectionReply(commandId, status);
}
void NetworkManager::notificationReceived(const QVariantMap &params)
{
qCDebug(dcNetworkManagement) << "Network management Notification:" << qUtf8Printable(QJsonDocument::fromVariant(params).toJson());
QString notification = params.value("notification").toString();
if (notification == "NetworkManager.WirelessNetworkDeviceChanged") {
if (notification == "NetworkManager.WirelessNetworkDeviceAdded") {
QVariantMap deviceMap = params.value("params").toMap().value("wirelessNetworkDevice").toMap();
WirelessNetworkDevice *device = new WirelessNetworkDevice(deviceMap.value("macAddress").toString(), deviceMap.value("interface").toString(), this);
device->setIpv4Addresses(deviceMap.value("ipv4Addresses").toStringList());
device->setIpv6Addresses(deviceMap.value("ipv6Addresses").toStringList());
device->setBitRate(deviceMap.value("bitRate").toString());
QMetaEnum stateEnum = QMetaEnum::fromType<NetworkDevice::NetworkDeviceState>();
device->setState(static_cast<NetworkDevice::NetworkDeviceState>(stateEnum.keyToValue(deviceMap.value("state").toString().toUtf8())));
QMetaEnum modeEnum = QMetaEnum::fromType<WirelessNetworkDevice::WirelessMode>();
device->setWirelessMode(static_cast<WirelessNetworkDevice::WirelessMode>(modeEnum.keyToValue(deviceMap.value("mode").toString().toUtf8())));
QVariantMap currentApMap = deviceMap.value("currentAccessPoint").toMap();
device->currentAccessPoint()->setSsid(currentApMap.value("ssid").toString());
device->currentAccessPoint()->setMacAddress(currentApMap.value("macAddress").toString());
device->currentAccessPoint()->setProtected(currentApMap.value("protected").toBool());
device->currentAccessPoint()->setSignalStrength(currentApMap.value("signalStrength").toInt());
device->currentAccessPoint()->setFrequency(currentApMap.value("frequency").toDouble());
m_wirelessNetworkDevices->addNetworkDevice(device);
} else if (notification == "NetworkManager.WirelessNetworkDeviceRemoved") {
QString interface = params.value("params").toMap().value("interface").toString();
m_wirelessNetworkDevices->removeNetworkDevice(interface);
} else if (notification == "NetworkManager.WirelessNetworkDeviceChanged") {
QVariantMap deviceMap = params.value("params").toMap().value("wirelessNetworkDevice").toMap();
WirelessNetworkDevice* device = m_wirelessNetworkDevices->getWirelessNetworkDevice(deviceMap.value("interface").toString());
if (!device) {
@ -335,16 +418,33 @@ void NetworkManager::notificationReceived(const QVariantMap &params)
device->currentAccessPoint()->setMacAddress(currentApMap.value("macAddress").toString());
device->currentAccessPoint()->setProtected(currentApMap.value("protected").toBool());
device->currentAccessPoint()->setSignalStrength(currentApMap.value("signalStrength").toInt());
} else if (notification == "NetworkManager.WiredNetworkDeviceAdded") {
QVariantMap deviceMap = params.value("params").toMap().value("wiredNetworkDevice").toMap();
WiredNetworkDevice *device = new WiredNetworkDevice(deviceMap.value("macAddress").toString(), deviceMap.value("interface").toString(), this);
device->setIpv4Addresses(deviceMap.value("ipv4Addresses").toStringList());
device->setIpv6Addresses(deviceMap.value("ipv6Addresses").toStringList());
device->setBitRate(deviceMap.value("bitRate").toString());
device->setPluggedIn(deviceMap.value("pluggedIn").toBool());
QMetaEnum stateEnum = QMetaEnum::fromType<NetworkDevice::NetworkDeviceState>();
device->setState(static_cast<NetworkDevice::NetworkDeviceState>(stateEnum.keyToValue(deviceMap.value("state").toString().toUtf8())));
m_wiredNetworkDevices->addWiredNetworkDevice(device);
} else if (notification == "NetworkManager.WiredNetworkDeviceRemoved") {
QString interface = params.value("params").toMap().value("interface").toString();
m_wiredNetworkDevices->removeNetworkDevice(interface);
} else if (notification == "NetworkManager.WiredNetworkDeviceChanged") {
QVariantMap deviceMap = params.value("params").toMap().value("wiredNetworkDevice").toMap();
WiredNetworkDevice* device = m_wiredNetworkDevices->getWiredNetworkDevice(deviceMap.value("interface").toString());
if (!device) {
qWarning() << "Received a notification for a network device we don't know" << deviceMap;
qCWarning(dcNetworkManagement) << "Received a notification for a network device we don't know" << deviceMap;
return;
}
device->setBitRate(deviceMap.value("bitRate").toString());
device->setIpv4Addresses(deviceMap.value("ipv4Addresses").toStringList());
device->setIpv6Addresses(deviceMap.value("ipv6Addresses").toStringList());
device->setBitRate(deviceMap.value("bitRate").toString());
device->setPluggedIn(deviceMap.value("pluggedIn").toBool());
QMetaEnum stateEnum = QMetaEnum::fromType<NetworkDevice::NetworkDeviceState>();
device->setState(static_cast<NetworkDevice::NetworkDeviceState>(stateEnum.keyToValue(deviceMap.value("state").toString().toUtf8())));
} else if (notification == "NetworkManager.NetworkStatusChanged") {
@ -366,6 +466,6 @@ void NetworkManager::notificationReceived(const QVariantMap &params)
}
} else {
qDebug() << "notification received" << qUtf8Printable(QJsonDocument::fromVariant(params).toJson(QJsonDocument::Indented));
qCWarning(dcNetworkManagement) << "Unhandled notification received" << qUtf8Printable(QJsonDocument::fromVariant(params).toJson(QJsonDocument::Indented));
}
}

View File

@ -90,6 +90,9 @@ public:
Q_INVOKABLE int connectToWiFi(const QString &interface, const QString &ssid, const QString &passphrase);
Q_INVOKABLE int startAccessPoint(const QString &interface, const QString &ssid, const QString &passphrase);
Q_INVOKABLE int createWiredAutoConnection(const QString &interface);
Q_INVOKABLE int createWiredManualConnection(const QString &interface, const QString &ip, quint8 prefix, const QString &gateway, const QString &dns);
Q_INVOKABLE int createWiredSharedConnection(const QString &interface, const QString &ip = QString(), quint8 prefix = 24);
Q_INVOKABLE int disconnectInterface(const QString &interface);
signals:
@ -105,6 +108,9 @@ signals:
void connectToWiFiReply(int id, const QString &status);
void disconnectReply(int id, const QString &status);
void startAccessPointReply(int id, const QString &status);
void createWiredAutoConnectionReply(int id, const QString &status);
void createWiredManualConnectionReply(int id, const QString &status);
void createWiredSharedConnectionReply(int id, const QString &status);
private slots:
void init();
@ -117,6 +123,9 @@ private slots:
void enableNetworkingResponse(int commandId, const QVariantMap &params);
void enableWirelessNetworkingResponse(int commandId, const QVariantMap &params);
void startAccessPointResponse(int commandId, const QVariantMap &params);
void createWiredAutoConnectionResponse(int commandId, const QVariantMap &params);
void createWiredManualConnectionResponse(int commandId, const QVariantMap &params);
void createWiredSharedConnectionResponse(int commandId, const QVariantMap &params);
void notificationReceived(const QVariantMap &params);

View File

@ -151,16 +151,13 @@ QVariant WiredNetworkDevices::data(const QModelIndex &index, int role) const
return NetworkDevices::data(index, role);
}
void WiredNetworkDevices::addNetworkDevice(NetworkDevice *device)
void WiredNetworkDevices::addWiredNetworkDevice(WiredNetworkDevice *device)
{
NetworkDevices::addNetworkDevice(device);
WiredNetworkDevice *wiredDev = qobject_cast<WiredNetworkDevice*>(device);
if (wiredDev) {
connect(wiredDev, &WiredNetworkDevice::pluggedInChanged, [this, wiredDev](){
emit dataChanged(index(m_list.indexOf(wiredDev)), index(m_list.indexOf(wiredDev)), {RolePluggedIn});
emit countChanged();
});
}
connect(device, &WiredNetworkDevice::pluggedInChanged, [this, device](){
emit dataChanged(index(m_list.indexOf(device)), index(m_list.indexOf(device)), {RolePluggedIn});
emit countChanged();
});
}
QHash<int, QByteArray> WiredNetworkDevices::roleNames() const

View File

@ -48,7 +48,8 @@ public:
RoleBitRate,
RoleState,
RoleIpv4Addresses,
RoleIpv6Addresses
RoleIpv6Addresses,
RolePluggedIn
};
Q_ENUM(Roles)
@ -86,7 +87,7 @@ public:
QVariant data(const QModelIndex &index, int role) const override;
QHash<int, QByteArray> roleNames() const override;
void addNetworkDevice(NetworkDevice *device) override;
void addWiredNetworkDevice(WiredNetworkDevice *device);
Q_INVOKABLE WiredNetworkDevice* getWiredNetworkDevice(const QString &interface);

View File

@ -48,6 +48,9 @@ SettingsPageBase {
onConnectToWiFiReply: handleReply(id, status)
onStartAccessPointReply: handleReply(id, status)
onDisconnectReply: handleReply(id, status)
onCreateWiredAutoConnectionReply: handleReply(id, status)
onCreateWiredManualConnectionReply: handleReply(id, status)
onCreateWiredSharedConnectionReply: handleReply(id, status)
function handleReply(id, status) {
if (id === d.pendingCallId) {
@ -86,7 +89,7 @@ SettingsPageBase {
}
var component = Qt.createComponent(Qt.resolvedUrl("../components/ErrorDialog.qml"))
var popup = component.createObject(root, {text: errorMessage, errorCode: stats})
var popup = component.createObject(root, {text: errorMessage, errorCode: status})
}
}
@ -154,7 +157,7 @@ SettingsPageBase {
visible: networkManager.available
}
NymeaSwipeDelegate {
NymeaItemDelegate {
Layout.fillWidth: true
text: qsTr("Current connection state")
prominentSubText: false
@ -203,7 +206,7 @@ SettingsPageBase {
}
}
NymeaSwipeDelegate {
NymeaItemDelegate {
Layout.fillWidth: true
text: qsTr("Networking enabled")
subText: qsTr("Enable or disable networking altogether")
@ -257,7 +260,7 @@ SettingsPageBase {
Repeater {
model: networkManager.wiredNetworkDevices
NymeaSwipeDelegate {
NymeaItemDelegate {
Layout.fillWidth: true
iconName: model.pluggedIn ? "../images/connections/network-wired.svg" : "../images/connections/network-wired-offline.svg"
text: model.interface + " (" + model.macAddress + ")"
@ -268,7 +271,19 @@ SettingsPageBase {
ret += networkStateToString(model.state)
return ret;
}
progressive: false
progressive: engine.jsonRpcClient.ensureServerVersion("6.2")
onClicked: {
if (!engine.jsonRpcClient.ensureServerVersion("6.2")) {
return;
}
var wiredNetworkDevice = networkManager.wiredNetworkDevices.getWiredNetworkDevice(model.interface);
if (wiredNetworkDevice.state === NetworkDevice.NetworkDeviceStateDisconnected) {
pageStack.push(createWiredConnectionPageComponent, {wiredNetworkDevice: wiredNetworkDevice})
} else {
pageStack.push(currentEthernetConnectionPageComponent, {wiredNetworkDevice: wiredNetworkDevice})
}
}
}
}
@ -277,7 +292,7 @@ SettingsPageBase {
visible: networkManager.available && networkManager.networkingEnabled
}
NymeaSwipeDelegate {
NymeaItemDelegate {
Layout.fillWidth: true
text: qsTr("Enabled")
subText: qsTr("Enable or disable WiFi")
@ -464,6 +479,138 @@ SettingsPageBase {
}
}
Component {
id: createWiredConnectionPageComponent
SettingsPageBase {
id: createWiredConnectionPage
title: qsTr("New wired connection")
property WiredNetworkDevice wiredNetworkDevice: null
SettingsPageSectionHeader {
text: qsTr("Method")
}
ColumnLayout {
Layout.fillWidth: true
Layout.leftMargin: app.margins
Layout.rightMargin: app.margins
spacing: 0
RadioButton {
id: dhcpClientRadioButton
Layout.fillWidth: true
checked: true
text: qsTr("Automatic (DHCP client)")
}
RadioButton {
id: manualClientRadioButton
Layout.fillWidth: true
text: qsTr("Manual")
}
RadioButton {
id: dhcpServerRadioButton
Layout.fillWidth: true
text: qsTr("Shared (DHCP server)")
}
}
SettingsPageSectionHeader {
text: qsTr("Address settings")
visible: manualClientRadioButton.checked
}
GridLayout {
Layout.fillWidth: true
Layout.leftMargin: app.margins
Layout.rightMargin: app.margins
columns: 2
visible: manualClientRadioButton.checked
Label {
text: qsTr("IP Address")
}
RowLayout {
TextField {
id: ipTextField
maximumLength: 32
Layout.fillWidth: true
horizontalAlignment: Text.AlignRight
validator: RegExpValidator {
regExp: /^((?:[0-1]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.){0,3}(?:[0-1]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$/
}
}
Label {
text: "/"
}
TextField {
id: prefixTextField
text: "24"
Layout.fillWidth: false
validator: IntValidator {
bottom: 8
top: 32
}
}
}
Label {
text: qsTr("Gateway")
}
TextField {
id: defaultGwTextField
maximumLength: 32
Layout.fillWidth: true
validator: RegExpValidator {
regExp: /^((?:[0-1]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.){0,3}(?:[0-1]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$/
}
}
Label {
text: qsTr("DNS")
}
TextField {
id: dnsTextField
maximumLength: 32
Layout.fillWidth: true
validator: RegExpValidator {
regExp: /^((?:[0-1]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.){0,3}(?:[0-1]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$/
}
}
}
Button {
Layout.fillWidth: true
Layout.margins: app.margins
Layout.leftMargin: app.margins
Layout.rightMargin: app.margins
text: qsTr("Create connection")
enabled: {
if (dhcpClientRadioButton.checked || dhcpServerRadioButton.checked) {
return true;
}
return ipTextField.acceptableInput && prefixTextField.acceptableInput
}
onClicked: {
if (dhcpClientRadioButton.checked) {
d.pendingCallId = networkManager.createWiredAutoConnection(createWiredConnectionPage.wiredNetworkDevice.interface)
} else if (manualClientRadioButton.checked) {
d.pendingCallId = networkManager.createWiredManualConnection(createWiredConnectionPage.wiredNetworkDevice.interface, ipTextField.text, prefixTextField.text, defaultGwTextField.text, dnsTextField.text)
} else if (dhcpServerRadioButton.checked) {
d.pendingCallId = networkManager.createWiredSharedConnection(createWiredConnectionPage.wiredNetworkDevice.interface)
}
pageStack.pop(root);
}
}
}
}
Component {
id: authPageComponent
@ -515,6 +662,50 @@ SettingsPageBase {
}
}
Component {
id: currentEthernetConnectionPageComponent
SettingsPageBase {
id: currentEthernetConnectionPage
title: qsTr("Current connection")
property WiredNetworkDevice wiredNetworkDevice: null
SettingsPageSectionHeader {
text: qsTr("Connected to")
}
NymeaItemDelegate {
Layout.fillWidth: true
text: qsTr("IPv4 Address")
subText: currentEthernetConnectionPage.wiredNetworkDevice.ipv4Addresses.join(", ")
progressive: false
}
NymeaItemDelegate {
Layout.fillWidth: true
text: qsTr("IPv6 Address")
subText: currentEthernetConnectionPage.wiredNetworkDevice.ipv6Addresses.join(", ")
visible: subText.length > 0
progressive: false
}
NymeaItemDelegate {
Layout.fillWidth: true
text: qsTr("MAC Address")
subText: currentEthernetConnectionPage.wiredNetworkDevice.macAddress
progressive: false
}
Button {
Layout.fillWidth: true
Layout.margins: app.margins
text: qsTr("Disconnect")
onClicked: {
d.pendingCallId = networkManager.disconnectInterface(currentEthernetConnectionPage.wiredNetworkDevice.interface)
pageStack.pop(root);
}
}
}
}
Component {
id: currentApPageComponent
SettingsPageBase {