From d6db31734729f602f8e9055fae6b8ac695321c10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Mon, 5 Mar 2018 20:37:40 +0100 Subject: [PATCH] Initial structure --- .gitignore | 73 +++ libnymea-networkmanager/dbus-interfaces.h | 28 + .../libnymea-networkmanager.pro | 38 ++ libnymea-networkmanager/networkconnection.cpp | 115 ++++ libnymea-networkmanager/networkconnection.h | 56 ++ libnymea-networkmanager/networkdevice.cpp | 330 ++++++++++++ libnymea-networkmanager/networkdevice.h | 206 ++++++++ libnymea-networkmanager/networkmanager.cpp | 421 +++++++++++++++ libnymea-networkmanager/networkmanager.h | 151 ++++++ libnymea-networkmanager/networksettings.cpp | 94 ++++ libnymea-networkmanager/networksettings.h | 49 ++ .../wirednetworkdevice.cpp | 87 +++ libnymea-networkmanager/wirednetworkdevice.h | 48 ++ .../wirelessaccesspoint.cpp | 141 +++++ libnymea-networkmanager/wirelessaccesspoint.h | 83 +++ .../wirelessnetworkdevice.cpp | 211 ++++++++ .../wirelessnetworkdevice.h | 74 +++ nymea-networkmanager.pri | 6 + nymea-networkmanager.pro | 5 + .../bluetooth/bluetoothserver.cpp | 439 +++++++++++++++ .../bluetooth/bluetoothserver.h | 103 ++++ .../bluetooth/bluetoothuuids.h | 35 ++ .../bluetooth/networkservice.cpp | 340 ++++++++++++ .../bluetooth/networkservice.h | 78 +++ .../bluetooth/systemservice.cpp | 256 +++++++++ .../bluetooth/systemservice.h | 61 +++ .../bluetooth/wirelessservice.cpp | 500 ++++++++++++++++++ .../bluetooth/wirelessservice.h | 101 ++++ nymea-networkmanager/core.cpp | 29 + nymea-networkmanager/core.h | 29 + nymea-networkmanager/main.cpp | 12 + nymea-networkmanager/nymea-networkmanager.pro | 31 ++ 32 files changed, 4230 insertions(+) create mode 100644 .gitignore create mode 100644 libnymea-networkmanager/dbus-interfaces.h create mode 100644 libnymea-networkmanager/libnymea-networkmanager.pro create mode 100644 libnymea-networkmanager/networkconnection.cpp create mode 100644 libnymea-networkmanager/networkconnection.h create mode 100644 libnymea-networkmanager/networkdevice.cpp create mode 100644 libnymea-networkmanager/networkdevice.h create mode 100644 libnymea-networkmanager/networkmanager.cpp create mode 100644 libnymea-networkmanager/networkmanager.h create mode 100644 libnymea-networkmanager/networksettings.cpp create mode 100644 libnymea-networkmanager/networksettings.h create mode 100644 libnymea-networkmanager/wirednetworkdevice.cpp create mode 100644 libnymea-networkmanager/wirednetworkdevice.h create mode 100644 libnymea-networkmanager/wirelessaccesspoint.cpp create mode 100644 libnymea-networkmanager/wirelessaccesspoint.h create mode 100644 libnymea-networkmanager/wirelessnetworkdevice.cpp create mode 100644 libnymea-networkmanager/wirelessnetworkdevice.h create mode 100644 nymea-networkmanager.pri create mode 100644 nymea-networkmanager.pro create mode 100644 nymea-networkmanager/bluetooth/bluetoothserver.cpp create mode 100644 nymea-networkmanager/bluetooth/bluetoothserver.h create mode 100644 nymea-networkmanager/bluetooth/bluetoothuuids.h create mode 100644 nymea-networkmanager/bluetooth/networkservice.cpp create mode 100644 nymea-networkmanager/bluetooth/networkservice.h create mode 100644 nymea-networkmanager/bluetooth/systemservice.cpp create mode 100644 nymea-networkmanager/bluetooth/systemservice.h create mode 100644 nymea-networkmanager/bluetooth/wirelessservice.cpp create mode 100644 nymea-networkmanager/bluetooth/wirelessservice.h create mode 100644 nymea-networkmanager/core.cpp create mode 100644 nymea-networkmanager/core.h create mode 100644 nymea-networkmanager/main.cpp create mode 100644 nymea-networkmanager/nymea-networkmanager.pro diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/libnymea-networkmanager/dbus-interfaces.h b/libnymea-networkmanager/dbus-interfaces.h new file mode 100644 index 0000000..0328825 --- /dev/null +++ b/libnymea-networkmanager/dbus-interfaces.h @@ -0,0 +1,28 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of loopd. * + * * + * Loopd can not be copied and/or distributed without the express * + * permission of guh GmbH. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef DBUSINTERFACES_H +#define DBUSINTERFACES_H + +#include +static const QString networkManagerServiceString("org.freedesktop.NetworkManager"); + +static const QString networkManagerPathString("/org/freedesktop/NetworkManager"); +static const QString settingsPathString("/org/freedesktop/NetworkManager/Settings"); + +static const QString deviceInterfaceString("org.freedesktop.NetworkManager.Device"); +static const QString wirelessInterfaceString("org.freedesktop.NetworkManager.Device.Wireless"); +static const QString wiredInterfaceString("org.freedesktop.NetworkManager.Device.Wired"); +static const QString accessPointInterfaceString("org.freedesktop.NetworkManager.AccessPoint"); +static const QString settingsInterfaceString("org.freedesktop.NetworkManager.Settings"); +static const QString connectionsInterfaceString("org.freedesktop.NetworkManager.Settings.Connection"); + +#endif // DBUSINTERFACES_H diff --git a/libnymea-networkmanager/libnymea-networkmanager.pro b/libnymea-networkmanager/libnymea-networkmanager.pro new file mode 100644 index 0000000..a83a83e --- /dev/null +++ b/libnymea-networkmanager/libnymea-networkmanager.pro @@ -0,0 +1,38 @@ +TARGET = nymea-networkmanager1 +TEMPLATE = lib + +target.path = /usr/lib/$$system('dpkg-architecture -q DEB_HOST_MULTIARCH') +INSTALLS += target + +QT += dbus network + +QMAKE_CXXFLAGS += -Werror -std=c++11 +QMAKE_LFLAGS += -std=c++11 + +HEADERS += \ + networkmanager.h \ + dbus-interfaces.h \ + networkconnection.h \ + networkdevice.h \ + networksettings.h \ + wirednetworkdevice.h \ + wirelessaccesspoint.h \ + wirelessnetworkdevice.h \ + +SOURCES += \ + networkmanager.cpp \ + networkconnection.cpp \ + networkdevice.cpp \ + networksettings.cpp \ + wirednetworkdevice.cpp \ + wirelessaccesspoint.cpp \ + wirelessnetworkdevice.cpp \ + + +# install header file with relative subdirectory +for(header, HEADERS) { + path = /usr/include/libnymea-networkmanager/$${dirname(header)} + eval(headers_$${path}.files += $${header}) + eval(headers_$${path}.path = $${path}) + eval(INSTALLS *= headers_$${path}) +} diff --git a/libnymea-networkmanager/networkconnection.cpp b/libnymea-networkmanager/networkconnection.cpp new file mode 100644 index 0000000..0e61735 --- /dev/null +++ b/libnymea-networkmanager/networkconnection.cpp @@ -0,0 +1,115 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of loopd. * + * * + * Loopd can not be copied and/or distributed without the express * + * permission of guh GmbH. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "networkconnection.h" +#include "dbus-interfaces.h" + +#include +#include + +/*! Constructs a new \l{NetworkConnection} with the given dbus \a objectPath and \a parent. */ +NetworkConnection::NetworkConnection(const QDBusObjectPath &objectPath, QObject *parent) : + QObject(parent), + m_objectPath(objectPath) +{ + qDBusRegisterMetaType(); + + m_connectionInterface = new QDBusInterface(networkManagerServiceString, m_objectPath.path(), connectionsInterfaceString, QDBusConnection::systemBus(), this); + if(!m_connectionInterface->isValid()) { + qWarning() << "Invalid connection dbus interface"; + return; + } + + QDBusMessage query = m_connectionInterface->call("GetSettings"); + if(query.type() != QDBusMessage::ReplyMessage) { + qWarning() << query.errorName() << query.errorMessage(); + return; + } + + if (query.arguments().isEmpty()) + return; + + const QDBusArgument &argument = query.arguments().at(0).value(); + m_connectionSettings = qdbus_cast(argument); +} + +/*! Delets this \l{NetworkConnection} in the \l{NetworkManager}. */ +void NetworkConnection::deleteConnection() +{ + QDBusMessage query = m_connectionInterface->call("Delete"); + if(query.type() != QDBusMessage::ReplyMessage) + qWarning() << query.errorName() << query.errorMessage(); + +} + +/*! Returns the dbus object path of this \l{NetworkConnection}. */ +QDBusObjectPath NetworkConnection::objectPath() const +{ + return m_objectPath; +} + +/*! Returns the connection settings of this \l{NetworkConnection}. */ +ConnectionSettings NetworkConnection::connectionSettings() const +{ + return m_connectionSettings; +} + +/*! Returns the id of this \l{NetworkConnection}. */ +QString NetworkConnection::id() const +{ + return m_connectionSettings.value("connection").value("id").toString(); +} + +/*! Returns the name of this \l{NetworkConnection}. */ +QString NetworkConnection::name() const +{ + return m_connectionSettings.value("connection").value("name").toString(); +} + +/*! Returns the type of this \l{NetworkConnection}. */ +QString NetworkConnection::type() const +{ + return m_connectionSettings.value("connection").value("type").toString(); +} + +/*! Returns the uuid of this \l{NetworkConnection}. */ +QUuid NetworkConnection::uuid() const +{ + return m_connectionSettings.value("connection").value("uuid").toUuid(); +} + +/*! Returns the interface name of this \l{NetworkConnection}. */ +QString NetworkConnection::interfaceName() const +{ + return m_connectionSettings.value("connection").value("interface-name").toString(); +} + +/*! Returns true if this \l{NetworkConnection} will autoconnect if available. */ +bool NetworkConnection::autoconnect() const +{ + return m_connectionSettings.value("connection").value("autoconnect").toBool(); +} + +/*! Returns the timestamp of this \l{NetworkConnection} from the last connection. */ +QDateTime NetworkConnection::timeStamp() const +{ + return QDateTime::fromTime_t(m_connectionSettings.value("connection").value("timestamp").toUInt()); +} + +QDebug operator<<(QDebug debug, NetworkConnection *networkConnection) +{ + debug.nospace() << "NetworkConnection(" << networkConnection->id() << ", "; + debug.nospace() << networkConnection->uuid().toString() << ", "; + debug.nospace() << networkConnection->interfaceName() << ", "; + debug.nospace() << networkConnection->type() << ", "; + debug.nospace() << networkConnection->timeStamp().toString("dd.MM.yyyy hh:mm") << ") "; + return debug; +} diff --git a/libnymea-networkmanager/networkconnection.h b/libnymea-networkmanager/networkconnection.h new file mode 100644 index 0000000..d118a82 --- /dev/null +++ b/libnymea-networkmanager/networkconnection.h @@ -0,0 +1,56 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of loopd. * + * * + * Loopd can not be copied and/or distributed without the express * + * permission of guh GmbH. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef NETWORKCONNECTION_H +#define NETWORKCONNECTION_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef QMap ConnectionSettings; + +class NetworkConnection : public QObject +{ + Q_OBJECT +public: + explicit NetworkConnection(const QDBusObjectPath &objectPath, QObject *parent = 0); + + void deleteConnection(); + + QDBusObjectPath objectPath() const; + ConnectionSettings connectionSettings() const; + + QString id() const; + QString name() const; + QString type() const; + QUuid uuid() const; + QString interfaceName() const; + bool autoconnect() const; + QDateTime timeStamp() const; + +private: + QDBusObjectPath m_objectPath; + QDBusInterface *m_connectionInterface; + + ConnectionSettings m_connectionSettings; + +}; + +QDebug operator<<(QDebug debug, NetworkConnection *networkConnection); + +#endif // NETWORKCONNECTION_H diff --git a/libnymea-networkmanager/networkdevice.cpp b/libnymea-networkmanager/networkdevice.cpp new file mode 100644 index 0000000..d7a133a --- /dev/null +++ b/libnymea-networkmanager/networkdevice.cpp @@ -0,0 +1,330 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of loopd. * + * * + * Loopd can not be copied and/or distributed without the express * + * permission of guh GmbH. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/*! \enum guhserver::NetworkDevice::NetworkDeviceState + \value NetworkDeviceStateUnknown + \value NetworkDeviceStateUnmanaged + \value NetworkDeviceStateUnavailable + \value NetworkDeviceStateDisconnected + \value NetworkDeviceStatePrepare + \value NetworkDeviceStateConfig + \value NetworkDeviceStateNeedAuth + \value NetworkDeviceStateIpConfig + \value NetworkDeviceStateIpCheck + \value NetworkDeviceStateSecondaries + \value NetworkDeviceStateActivated + \value NetworkDeviceStateDeactivating + \value NetworkDeviceStateFailed +*/ + +/*! \enum guhserver::NetworkDevice::NetworkDeviceStateReason + \value NetworkDeviceStateReasonNone + \value NetworkDeviceStateReasonUnknown + \value NetworkDeviceStateReasonNowManaged + \value NetworkDeviceStateReasonNowUnmanaged + \value NetworkDeviceStateReasonConfigFailed + \value NetworkDeviceStateReasonIpConfigUnavailable + \value NetworkDeviceStateReasonIpConfigExpired + \value NetworkDeviceStateReasonNoSecrets + \value NetworkDeviceStateReasonSupplicantDisconnected + \value NetworkDeviceStateReasonSupplicantConfigFailed + \value NetworkDeviceStateReasonSupplicantFailed + \value NetworkDeviceStateReasonSupplicantTimeout + \value NetworkDeviceStateReasonPppStartFailed + \value NetworkDeviceStateReasonPppDisconnected + \value NetworkDeviceStateReasonPppFailed + \value NetworkDeviceStateReasonDhcpStartFailed + \value NetworkDeviceStateReasonDhcpError + \value NetworkDeviceStateReasonDhcpFailed + \value NetworkDeviceStateReasonSharedStartFailed + \value NetworkDeviceStateReasonSharedFailed + \value NetworkDeviceStateReasonAutoIpStartFailed + \value NetworkDeviceStateReasonAutoIpError + \value NetworkDeviceStateReasonAutoIpFailed + \value NetworkDeviceStateReasonModemBusy + \value NetworkDeviceStateReasonModemNoDialTone + \value NetworkDeviceStateReasonModemNoCarrier + \value NetworkDeviceStateReasonModemDialTimeout + \value NetworkDeviceStateReasonModemDialFailed + \value NetworkDeviceStateReasonModemInitFailed + \value NetworkDeviceStateReasonGsmApnFailed + \value NetworkDeviceStateReasonGsmRegistrationNotSearching + \value NetworkDeviceStateReasonGsmRegistrationDenied + \value NetworkDeviceStateReasonGsmRegistrationTimeout + \value NetworkDeviceStateReasonGsmRegistrationFailed + \value NetworkDeviceStateReasonGsmPinCheckFailed + \value NetworkDeviceStateReasonFirmwareMissing + \value NetworkDeviceStateReasonRemoved + \value NetworkDeviceStateReasonSleeping + \value NetworkDeviceStateReasonConnectionRemoved + \value NetworkDeviceStateReasonUserRequest + \value NetworkDeviceStateReasonCarrier + \value NetworkDeviceStateReasonConnectionAssumed + \value NetworkDeviceStateReasonSupplicantAvailable + \value NetworkDeviceStateReasonModemNotFound + \value NetworkDeviceStateReasonBtFailed + \value NetworkDeviceStateReasonGsmSimNotInserted + \value NetworkDeviceStateReasonGsmSimPinRequired + \value NetworkDeviceStateReasonGsmSimPukRequired + \value NetworkDeviceStateReasonGsmSimWrong + \value NetworkDeviceStateReasonInfinibandMode + \value NetworkDeviceStateReasonDependencyFailed + \value NetworkDeviceStateReasonBR2684Failed + \value NetworkDeviceStateReasonModemManagerUnavailable + \value NetworkDeviceStateReasonSsidNotFound + \value NetworkDeviceStateReasonSecondaryConnectionFailed + \value NetworkDeviceStateReasonDcbFoecFailed + \value NetworkDeviceStateReasonTeamdControlFailed + \value NetworkDeviceStateReasonModemFailed + \value NetworkDeviceStateReasonModemAvailable + \value NetworkDeviceStateReasonSimPinIncorrect + \value NetworkDeviceStateReasonNewActivision + \value NetworkDeviceStateReasonParentChanged + \value NetworkDeviceStateReasonParentManagedChanged +*/ + + +/*! \enum guhserver::NetworkDevice::NetworkDeviceType + \value NetworkDeviceTypeUnknown + \value NetworkDeviceTypeEthernet + \value NetworkDeviceTypeWifi + \value NetworkDeviceTypeBluetooth + \value NetworkDeviceTypeOlpcMesh + \value NetworkDeviceTypeWiMax + \value NetworkDeviceTypeModem + \value NetworkDeviceTypeInfiniBand + \value NetworkDeviceTypeBond + \value NetworkDeviceTypeVLan + \value NetworkDeviceTypeAdsl + \value NetworkDeviceTypeBridge + \value NetworkDeviceTypeGeneric + \value NetworkDeviceTypeTeam + \value NetworkDeviceTypeTun + \value NetworkDeviceTypeIpTunnel + \value NetworkDeviceTypeMacVLan + \value NetworkDeviceTypeVXLan + \value NetworkDeviceTypeVEth +*/ + + +/*! \fn void NetworkDevice::deviceChanged(); + This signal will be emitted when the properties of this \l{NetworkDevice} have changed. +*/ + + +#include "networkdevice.h" + +#include +#include + +/*! Constructs a new \l{NetworkDevice} with the given dbus \a objectPath and \a parent. */ +NetworkDevice::NetworkDevice(const QDBusObjectPath &objectPath, QObject *parent) : + QObject(parent), + m_objectPath(objectPath), + m_mtu(0), + m_metered(0), + m_deviceState(NetworkDeviceStateUnknown), + m_deviceStateReason(NetworkDeviceStateReasonUnknown) +{ + QDBusConnection systemBus = QDBusConnection::systemBus(); + if (!systemBus.isConnected()) { + qWarning() << "NetworkDevice: System DBus not connected"; + return; + } + + m_networkDeviceInterface = new QDBusInterface(networkManagerServiceString, m_objectPath.path(), deviceInterfaceString, QDBusConnection::systemBus(), this); + if(!m_networkDeviceInterface->isValid()) { + qWarning() << "NetworkDevice: Invalid DBus device interface" << m_objectPath.path(); + return; + } + + m_udi = m_networkDeviceInterface->property("Udi").toString(); + m_interface = m_networkDeviceInterface->property("Interface").toString(); + m_ipInterface = m_networkDeviceInterface->property("IpInterface").toString(); + m_driver = m_networkDeviceInterface->property("Driver").toString(); + m_driverVersion = m_networkDeviceInterface->property("DriverVersion").toString(); + m_firmwareVersion = m_networkDeviceInterface->property("FirmwareVersion").toString(); + m_physicalPortId = m_networkDeviceInterface->property("PhysicalPortId").toString(); + m_mtu = m_networkDeviceInterface->property("Mtu").toUInt(); + m_metered = m_networkDeviceInterface->property("Metered").toUInt(); + m_autoconnect = m_networkDeviceInterface->property("Autoconnect").toBool(); + + m_deviceState = NetworkDeviceState(m_networkDeviceInterface->property("State").toUInt()); + m_deviceType = NetworkDeviceType(m_networkDeviceInterface->property("DeviceType").toUInt()); + + m_activeConnection = qdbus_cast(m_networkDeviceInterface->property("ActiveConnection")); + m_ip4Config = qdbus_cast(m_networkDeviceInterface->property("Ip4Config")); + m_ip6Config = qdbus_cast(m_networkDeviceInterface->property("Ip6Config")); + + QDBusConnection::systemBus().connect(networkManagerServiceString, m_objectPath.path(), deviceInterfaceString, "StateChanged", this, SLOT(onStateChanged(uint,uint,uint))); +} + +/*! Returns the dbus object path of this \l{NetworkDevice}. */ +QDBusObjectPath NetworkDevice::objectPath() const +{ + return m_objectPath; +} + +/*! Returns the udi of this \l{NetworkDevice}. */ +QString NetworkDevice::udi() const +{ + return m_udi; +} + +/*! Returns the interface name of this \l{NetworkDevice}. */ +QString NetworkDevice::interface() const +{ + return m_interface; +} + +/*! Returns the ip interface of this \l{NetworkDevice}. */ +QString NetworkDevice::ipInterface() const +{ + return m_ipInterface; +} + +/*! Returns the used driver name for this \l{NetworkDevice}. */ +QString NetworkDevice::driver() const +{ + return m_driver; +} + +/*! Returns the version of the used driver for this \l{NetworkDevice}. */ +QString NetworkDevice::driverVersion() const +{ + return m_driverVersion; +} + +/*! Returns the firmware version of this \l{NetworkDevice}. */ +QString NetworkDevice::firmwareVersion() const +{ + return m_firmwareVersion; +} + +/*! Returns the physical port id of this \l{NetworkDevice}. */ +QString NetworkDevice::physicalPortId() const +{ + return m_physicalPortId; +} + +/*! Returns the mtu of this \l{NetworkDevice}. */ +uint NetworkDevice::mtu() const +{ + return m_mtu; +} + +/*! Returns the metered property of this \l{NetworkDevice}. */ +uint NetworkDevice::metered() const +{ + return m_metered; +} + +/*! Returns true if autoconnect is enabled for this \l{NetworkDevice}. */ +bool NetworkDevice::autoconnect() const +{ + return m_autoconnect; +} + +/*! Returns the device state of this \l{NetworkDevice}. \sa NetworkDeviceState, */ +NetworkDevice::NetworkDeviceState NetworkDevice::deviceState() const +{ + return m_deviceState; +} + +/*! Returns the human readable device state of this \l{NetworkDevice}. \sa NetworkDeviceState, */ +QString NetworkDevice::deviceStateString() const +{ + return NetworkDevice::deviceStateToString(m_deviceState); +} + +/*! Returns the reason for the current state of this \l{NetworkDevice}. \sa NetworkDeviceStateReason, */ +NetworkDevice::NetworkDeviceStateReason NetworkDevice::deviceStateReason() const +{ + return m_deviceStateReason; +} + +/*! Returns the device type of this \l{NetworkDevice}. \sa NetworkDeviceType, */ +NetworkDevice::NetworkDeviceType NetworkDevice::deviceType() const +{ + return m_deviceType; +} + +/*! Returns the dbus object path of the currently active connection of this \l{NetworkDevice}. */ +QDBusObjectPath NetworkDevice::activeConnection() const +{ + return m_activeConnection; +} + +/*! Returns the dbus object path from the IPv4 configuration of this \l{NetworkDevice}. */ +QDBusObjectPath NetworkDevice::ip4Config() const +{ + return m_ip4Config; +} + +/*! Returns the list of dbus object paths for the currently available connection of this \l{NetworkDevice}. */ +QList NetworkDevice::availableConnections() const +{ + return m_availableConnections; +} + +/*! Disconnect the current connection from this \l{NetworkDevice}. */ +void NetworkDevice::disconnectDevice() +{ + QDBusMessage query = m_networkDeviceInterface->call("Disconnect"); + if(query.type() != QDBusMessage::ReplyMessage) + qWarning() << query.errorName() << query.errorMessage(); + +} + +/*! Returns the human readable device type string of the given \a deviceType. \sa NetworkDeviceType, */ +QString NetworkDevice::deviceTypeToString(const NetworkDevice::NetworkDeviceType &deviceType) +{ + QMetaObject metaObject = NetworkDevice::staticMetaObject; + int enumIndex = metaObject.indexOfEnumerator(QString("NetworkDeviceType").toLatin1().data()); + QMetaEnum metaEnum = metaObject.enumerator(enumIndex); + return QString(metaEnum.valueToKey(deviceType)).remove("NetworkDeviceType"); +} + +/*! Returns the human readable device state string of the given \a deviceState. \sa NetworkDeviceState, */ +QString NetworkDevice::deviceStateToString(const NetworkDevice::NetworkDeviceState &deviceState) +{ + QMetaObject metaObject = NetworkDevice::staticMetaObject; + int enumIndex = metaObject.indexOfEnumerator(QString("NetworkDeviceState").toLatin1().data()); + QMetaEnum metaEnum = metaObject.enumerator(enumIndex); + return QString(metaEnum.valueToKey(deviceState)); +} + +/*! Returns the human readable device state reason string of the given \a deviceStateReason. \sa NetworkDeviceStateReason, */ +QString NetworkDevice::deviceStateReasonToString(const NetworkDevice::NetworkDeviceStateReason &deviceStateReason) +{ + QMetaObject metaObject = NetworkDevice::staticMetaObject; + int enumIndex = metaObject.indexOfEnumerator(QString("NetworkDeviceStateReason").toLatin1().data()); + QMetaEnum metaEnum = metaObject.enumerator(enumIndex); + return QString(metaEnum.valueToKey(deviceStateReason)); +} + +void NetworkDevice::onStateChanged(uint newState, uint oldState, uint reason) +{ + Q_UNUSED(oldState); + qDebug() << m_interface << "--> State changed:" << deviceStateToString(NetworkDeviceState(newState)) << ":" << deviceStateReasonToString(NetworkDeviceStateReason(reason)); + if (m_deviceState != NetworkDeviceState(newState)) { + m_deviceState = NetworkDeviceState(newState); + emit deviceChanged(); + emit stateChanged(m_deviceState); + } +} + +QDebug operator<<(QDebug debug, NetworkDevice *device) +{ + debug.nospace() << "NetworkDevice(" << device->interface() << " - " << NetworkDevice::deviceTypeToString(device->deviceType()) << ", " << device->deviceStateString() << ")"; + return debug; +} + diff --git a/libnymea-networkmanager/networkdevice.h b/libnymea-networkmanager/networkdevice.h new file mode 100644 index 0000000..4e5e1d7 --- /dev/null +++ b/libnymea-networkmanager/networkdevice.h @@ -0,0 +1,206 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of loopd. * + * * + * Loopd can not be copied and/or distributed without the express * + * permission of guh GmbH. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef NETWORKDEVICE_H +#define NETWORKDEVICE_H + +#include +#include +#include +#include +#include +#include +#include + +#include "dbus-interfaces.h" + +class NetworkDevice : public QObject +{ + Q_OBJECT + Q_ENUMS(NetworkDeviceType) + Q_ENUMS(NetworkDeviceState) + Q_ENUMS(NetworkDeviceStateReason) + +public: + enum NetworkDeviceState { + NetworkDeviceStateUnknown = 0, + NetworkDeviceStateUnmanaged = 10, + NetworkDeviceStateUnavailable = 20, + NetworkDeviceStateDisconnected = 30, + NetworkDeviceStatePrepare = 40, + NetworkDeviceStateConfig = 50, + NetworkDeviceStateNeedAuth = 60, + NetworkDeviceStateIpConfig = 70, + NetworkDeviceStateIpCheck = 80, + NetworkDeviceStateSecondaries = 90, + NetworkDeviceStateActivated = 100, + NetworkDeviceStateDeactivating = 110, + NetworkDeviceStateFailed = 120 + }; + + enum NetworkDeviceStateReason { + NetworkDeviceStateReasonNone = 0, + NetworkDeviceStateReasonUnknown = 1, + NetworkDeviceStateReasonNowManaged = 2, + NetworkDeviceStateReasonNowUnmanaged = 3, + NetworkDeviceStateReasonConfigFailed = 4, + NetworkDeviceStateReasonIpConfigUnavailable = 5, + NetworkDeviceStateReasonIpConfigExpired = 6, + NetworkDeviceStateReasonNoSecrets = 7, + NetworkDeviceStateReasonSupplicantDisconnected = 8, + NetworkDeviceStateReasonSupplicantConfigFailed = 9, + NetworkDeviceStateReasonSupplicantFailed = 10, + NetworkDeviceStateReasonSupplicantTimeout = 11, + NetworkDeviceStateReasonPppStartFailed = 12, + NetworkDeviceStateReasonPppDisconnected = 13, + NetworkDeviceStateReasonPppFailed = 14, + NetworkDeviceStateReasonDhcpStartFailed = 15, + NetworkDeviceStateReasonDhcpError = 16, + NetworkDeviceStateReasonDhcpFailed = 17, + NetworkDeviceStateReasonSharedStartFailed = 18, + NetworkDeviceStateReasonSharedFailed = 19, + NetworkDeviceStateReasonAutoIpStartFailed = 20, + NetworkDeviceStateReasonAutoIpError = 21, + NetworkDeviceStateReasonAutoIpFailed = 22, + NetworkDeviceStateReasonModemBusy = 23, + NetworkDeviceStateReasonModemNoDialTone = 24, + NetworkDeviceStateReasonModemNoCarrier = 25, + NetworkDeviceStateReasonModemDialTimeout = 26, + NetworkDeviceStateReasonModemDialFailed = 27, + NetworkDeviceStateReasonModemInitFailed = 28, + NetworkDeviceStateReasonGsmApnFailed = 29, + NetworkDeviceStateReasonGsmRegistrationNotSearching = 30, + NetworkDeviceStateReasonGsmRegistrationDenied = 31, + NetworkDeviceStateReasonGsmRegistrationTimeout = 32, + NetworkDeviceStateReasonGsmRegistrationFailed = 33, + NetworkDeviceStateReasonGsmPinCheckFailed = 34, + NetworkDeviceStateReasonFirmwareMissing = 35, + NetworkDeviceStateReasonRemoved = 36, + NetworkDeviceStateReasonSleeping = 37, + NetworkDeviceStateReasonConnectionRemoved = 38, + NetworkDeviceStateReasonUserRequest = 39, + NetworkDeviceStateReasonCarrier = 40, + NetworkDeviceStateReasonConnectionAssumed = 41, + NetworkDeviceStateReasonSupplicantAvailable = 42, + NetworkDeviceStateReasonModemNotFound = 43, + NetworkDeviceStateReasonBtFailed = 44, + NetworkDeviceStateReasonGsmSimNotInserted = 45, + NetworkDeviceStateReasonGsmSimPinRequired = 46, + NetworkDeviceStateReasonGsmSimPukRequired = 47, + NetworkDeviceStateReasonGsmSimWrong = 48, + NetworkDeviceStateReasonInfinibandMode = 49, + NetworkDeviceStateReasonDependencyFailed = 50, + NetworkDeviceStateReasonBR2684Failed = 51, + NetworkDeviceStateReasonModemManagerUnavailable = 52, + NetworkDeviceStateReasonSsidNotFound = 53, + NetworkDeviceStateReasonSecondaryConnectionFailed = 54, + NetworkDeviceStateReasonDcbFoecFailed = 55, + NetworkDeviceStateReasonTeamdControlFailed = 56, + NetworkDeviceStateReasonModemFailed = 57, + NetworkDeviceStateReasonModemAvailable = 58, + NetworkDeviceStateReasonSimPinIncorrect = 59, + NetworkDeviceStateReasonNewActivision = 60, + NetworkDeviceStateReasonParentChanged = 61, + NetworkDeviceStateReasonParentManagedChanged = 62 + }; + + enum NetworkDeviceType { + NetworkDeviceTypeUnknown = 0, + NetworkDeviceTypeEthernet = 1, + NetworkDeviceTypeWifi = 2, + NetworkDeviceTypeBluetooth = 5, + NetworkDeviceTypeOlpcMesh = 6, + NetworkDeviceTypeWiMax = 7, + NetworkDeviceTypeModem = 8, + NetworkDeviceTypeInfiniBand = 9, + NetworkDeviceTypeBond = 10, + NetworkDeviceTypeVLan = 11, + NetworkDeviceTypeAdsl = 12, + NetworkDeviceTypeBridge = 13, + NetworkDeviceTypeGeneric = 14, + NetworkDeviceTypeTeam = 15, + NetworkDeviceTypeTun = 16, + NetworkDeviceTypeIpTunnel = 17, + NetworkDeviceTypeMacVLan = 18, + NetworkDeviceTypeVXLan = 19, + NetworkDeviceTypeVEth = 20, + }; + + explicit NetworkDevice(const QDBusObjectPath &objectPath, QObject *parent = 0); + + QDBusObjectPath objectPath() const; + + QString udi() const; + QString interface() const; + QString ipInterface() const; + QString driver() const; + QString driverVersion() const; + QString firmwareVersion() const; + QString physicalPortId() const; + uint mtu() const; + uint metered() const; + bool autoconnect() const; + + NetworkDeviceState deviceState() const; + QString deviceStateString() const; + + NetworkDeviceStateReason deviceStateReason() const; + NetworkDeviceType deviceType() const; + + QDBusObjectPath activeConnection() const; + QDBusObjectPath ip4Config() const; + QList availableConnections() const; + + // Method + void disconnectDevice(); + + static QString deviceTypeToString(const NetworkDeviceType &deviceType); + static QString deviceStateToString(const NetworkDeviceState &deviceState); + static QString deviceStateReasonToString(const NetworkDeviceStateReason &deviceStateReason); + +private: + QDBusInterface *m_networkDeviceInterface; + QDBusObjectPath m_objectPath; + + // Device properties + QString m_udi; + QString m_interface; + QString m_ipInterface; + QString m_driver; + QString m_driverVersion; + QString m_firmwareVersion; + QString m_physicalPortId; + uint m_mtu; + uint m_metered; + bool m_autoconnect; + NetworkDeviceState m_deviceState; + NetworkDeviceStateReason m_deviceStateReason; + NetworkDeviceType m_deviceType; + + QDBusObjectPath m_activeConnection; + QDBusObjectPath m_ip4Config; + QDBusObjectPath m_ip6Config; + + QList m_availableConnections; + +private slots: + void onStateChanged(uint newState, uint oldState, uint reason); + +signals: + void deviceChanged(); + void stateChanged(const NetworkDeviceState &state); + + +}; + +QDebug operator<<(QDebug debug, NetworkDevice *device); + +#endif // NETWORKDEVICE_H diff --git a/libnymea-networkmanager/networkmanager.cpp b/libnymea-networkmanager/networkmanager.cpp new file mode 100644 index 0000000..6e2f693 --- /dev/null +++ b/libnymea-networkmanager/networkmanager.cpp @@ -0,0 +1,421 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of loopd. * + * * + * Loopd can not be copied and/or distributed without the express * + * permission of guh GmbH. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "networkmanager.h" +#include "networkconnection.h" + +#include +#include +#include + +/*! Constructs a new \l{NetworkManager} object with the given \a parent. */ +NetworkManager::NetworkManager(QObject *parent) : + QObject(parent), + m_networkManagerInterface(nullptr), + m_available(false), + m_state(NetworkManagerStateUnknown), + m_connectivityState(NetworkManagerConnectivityStateUnknown), + m_networkingEnabled(false), + m_wirelessEnabled(false) +{ + // Check DBus connection + if (!QDBusConnection::systemBus().isConnected()) { + qWarning() << "System DBus not connected. NetworkManagre not available."; + return; + } + + // Create interface + m_networkManagerInterface = new QDBusInterface(networkManagerServiceString, networkManagerPathString, networkManagerServiceString, QDBusConnection::systemBus(), this); + if(!m_networkManagerInterface->isValid()) { + qWarning() << "Invalid DBus network manager interface. NetworkManagre not available."; + return; + } + + m_available = true; + + // Read properties + setVersion(m_networkManagerInterface->property("Version").toString()); + setState((NetworkManagerState)m_networkManagerInterface->property("State").toUInt()); + setConnectivityState((NetworkManagerConnectivityState)m_networkManagerInterface->property("Connectivity").toUInt()); + setNetworkingEnabled(m_networkManagerInterface->property("NetworkingEnabled").toBool()); + setWirelessEnabled(m_networkManagerInterface->property("WirelessEnabled").toBool()); + + loadDevices(); + + // Connect signals + QDBusConnection::systemBus().connect(networkManagerServiceString, networkManagerPathString, networkManagerServiceString, "StateChanged", this, SLOT(onStateChanged(uint))); + QDBusConnection::systemBus().connect(networkManagerServiceString, networkManagerPathString, networkManagerServiceString, "DeviceAdded", this, SLOT(onDeviceAdded(QDBusObjectPath))); + QDBusConnection::systemBus().connect(networkManagerServiceString, networkManagerPathString, networkManagerServiceString, "DeviceRemoved", this, SLOT(onDeviceRemoved(QDBusObjectPath))); + QDBusConnection::systemBus().connect(networkManagerServiceString, networkManagerPathString, networkManagerServiceString, "PropertiesChanged", this, SLOT(onPropertiesChanged(QVariantMap))); + + // Create settings + m_networkSettings = new NetworkSettings(this); +} + +NetworkManager::~NetworkManager() +{ + qDebug() << "Destroy network manager."; +} + +/*! Returns true if the network-manager is available on this system. */ +bool NetworkManager::available() +{ + return m_available; +} + +/*! Returns true if wifi is available on this system. */ +bool NetworkManager::wirelessAvailable() +{ + return !m_wirelessNetworkDevices.isEmpty(); +} + +/*! Returns the list of \l{NetworkDevice}{NetworkDevices} from this \l{NetworkManager}. */ +QList NetworkManager::networkDevices() const +{ + return m_networkDevices.values(); +} + +QList NetworkManager::wirelessNetworkDevices() const +{ + return m_wirelessNetworkDevices.values(); +} + +/*! Returns the list of \l{WiredNetworkDevice}{WiredNetworkDevices} from this \l{NetworkManager}. */ +QList NetworkManager::wiredNetworkDevices() const +{ + return m_wiredNetworkDevices.values(); +} + +/*! Returns the \l{NetworkDevice} with the given \a interface from this \l{NetworkManager}. If there is no such \a interface returns Q_NULLPTR. */ +NetworkDevice *NetworkManager::getNetworkDevice(const QString &interface) +{ + foreach (NetworkDevice *device, m_networkDevices.values()) { + if (device->interface() == interface) + return device; + } + return Q_NULLPTR; +} + +/*! Returns the version of the running \l{NetworkManager}. */ +QString NetworkManager::version() const +{ + return m_version; +} + +/*! Returns the state of this \l{NetworkManager}. \sa NetworkManagerState, */ +NetworkManager::NetworkManagerState NetworkManager::state() const +{ + return m_state; +} + +/*! Returns the human readable string of the current state of this \l{NetworkManager}. \sa NetworkManagerState, */ +QString NetworkManager::stateString() const +{ + return networkManagerStateToString(m_state); +} + +/*! Returns the current connectivity state of this \l{NetworkManager}. \sa NetworkManagerConnectivityState, */ +NetworkManager::NetworkManagerConnectivityState NetworkManager::connectivityState() const +{ + return m_connectivityState; +} + +/*! Connect the given \a interface to a wifi network with the given \a ssid and \a password. Returns the \l{NetworkManagerError} to inform about the result. \sa NetworkManagerError, */ +NetworkManager::NetworkManagerError NetworkManager::connectWifi(const QString &interface, const QString &ssid, const QString &password, const bool &hidden) +{ + // Check interface + if (!getNetworkDevice(interface)) + return NetworkManagerErrorNetworkInterfaceNotFound; + + // Get wirelessNetworkDevice + WirelessNetworkDevice *wirelessNetworkDevice = 0; + foreach (WirelessNetworkDevice *networkDevice, wirelessNetworkDevices()) { + if (networkDevice->interface() == interface) + wirelessNetworkDevice = networkDevice; + } + + if (!wirelessNetworkDevice) + return NetworkManagerErrorInvalidNetworkDeviceType; + + // Get the access point object path + WirelessAccessPoint *accessPoint = wirelessNetworkDevice->getAccessPoint(ssid); + if (!accessPoint) + return NetworkManagerErrorAccessPointNotFound; + + // Note: https://developer.gnome.org/NetworkManager/stable/ref-settings.html + + QVariantMap connectionSettings; + connectionSettings.insert("autoconnect", true); + connectionSettings.insert("id", ssid + " (loop)"); + connectionSettings.insert("uuid", QUuid::createUuid().toString().remove("{").remove("}")); + connectionSettings.insert("type", "802-11-wireless"); + + QVariantMap wirelessSettings; + wirelessSettings.insert("ssid", ssid.toUtf8()); + wirelessSettings.insert("mode", "infrastructure"); + + // Note: disable power save mode + wirelessSettings.insert("powersave", 2); + + if (hidden) + wirelessSettings.insert("hidden", true); + + QVariantMap wirelessSecuritySettings; + wirelessSecuritySettings.insert("auth-alg", "open"); + wirelessSecuritySettings.insert("key-mgmt", "wpa-psk"); + wirelessSecuritySettings.insert("psk", password); + + QVariantMap ipv4Settings; + ipv4Settings.insert("method", "auto"); + + QVariantMap ipv6Settings; + ipv6Settings.insert("method", "auto"); + + // Build connection object + ConnectionSettings settings; + settings.insert("connection", connectionSettings); + settings.insert("802-11-wireless", wirelessSettings); + settings.insert("ipv4", ipv4Settings); + settings.insert("ipv6", ipv6Settings); + settings.insert("802-11-wireless-security", wirelessSecuritySettings); + + // Remove old configuration (if there is any) + foreach (NetworkConnection *connection, m_networkSettings->connections()) { + if (connection->id() == connectionSettings.value("id")) { + connection->deleteConnection(); + } + } + + // Add connection + QDBusObjectPath connectionObjectPath = m_networkSettings->addConnection(settings); + if (connectionObjectPath.path().isEmpty()) + return NetworkManagerErrorWirelessConnectionFailed; + + // Activate connection + QDBusMessage query = m_networkManagerInterface->call("ActivateConnection", QVariant::fromValue(connectionObjectPath), QVariant::fromValue(wirelessNetworkDevice->objectPath()), QVariant::fromValue(accessPoint->objectPath())); + if(query.type() != QDBusMessage::ReplyMessage) { + qWarning() << query.errorName() << query.errorMessage(); + return NetworkManagerErrorWirelessConnectionFailed; + } + + return NetworkManagerErrorNoError; +} + +/*! Returns true if the networking of this \l{NetworkManager} is enabled. */ +bool NetworkManager::networkingEnabled() const +{ + return m_networkingEnabled; +} + +/*! Returns true if the networking of this \l{NetworkManager} could be \a enabled. */ +bool NetworkManager::enableNetworking(const bool &enabled) +{ + if (m_networkingEnabled == enabled) + return true; + + QDBusMessage query = m_networkManagerInterface->call("Enable", enabled); + if(query.type() != QDBusMessage::ReplyMessage) { + qWarning() << query.errorName() << query.errorMessage(); + return false; + } + return true; +} + +/*! Sets the networking of this \l{NetworkManager} to \a enabled. */ +void NetworkManager::setNetworkingEnabled(const bool &enabled) +{ + qDebug() << "Networking" << (enabled ? "enabled" : "disabled"); + m_networkingEnabled = enabled; + emit networkingEnabledChanged(); +} + +/*! Returns true if the wireless networking of this \l{NetworkManager} is enabled. */ +bool NetworkManager::wirelessEnabled() const +{ + return m_wirelessEnabled; +} + +/*! Returns true if the wireless networking of this \l{NetworkManager} could be set to \a enabled. */ +bool NetworkManager::enableWireless(const bool &enabled) +{ + if (m_wirelessEnabled == enabled) + return true; + + return m_networkManagerInterface->setProperty("WirelessEnabled", enabled); +} + +void NetworkManager::loadDevices() +{ + // Get network devices + QDBusMessage query = m_networkManagerInterface->call("GetDevices"); + if(query.type() != QDBusMessage::ReplyMessage) { + qWarning() << query.errorName() << query.errorMessage(); + return; + } + + if (query.arguments().isEmpty()) + return; + + const QDBusArgument &argument = query.arguments().at(0).value(); + argument.beginArray(); + while(!argument.atEnd()) { + QDBusObjectPath deviceObjectPath = qdbus_cast(argument); + onDeviceAdded(deviceObjectPath); + } + argument.endArray(); +} + +QString NetworkManager::networkManagerStateToString(const NetworkManager::NetworkManagerState &state) +{ + QMetaObject metaObject = NetworkManager::staticMetaObject; + int enumIndex = metaObject.indexOfEnumerator(QString("NetworkManagerState").toLatin1().data()); + QMetaEnum metaEnum = metaObject.enumerator(enumIndex); + return QString(metaEnum.valueToKey(state)); +} + +QString NetworkManager::networkManagerConnectivityStateToString(const NetworkManager::NetworkManagerConnectivityState &state) +{ + QMetaObject metaObject = NetworkManager::staticMetaObject; + int enumIndex = metaObject.indexOfEnumerator(QString("NetworkManagerConnectivityState").toLatin1().data()); + QMetaEnum metaEnum = metaObject.enumerator(enumIndex); + return QString(metaEnum.valueToKey(state)).remove("NetworkManagerConnectivityState"); +} + +void NetworkManager::setVersion(const QString &version) +{ + qDebug() << "Version:" << version; + m_version = version; + emit versionChanged(); +} + +void NetworkManager::setWirelessEnabled(const bool &enabled) +{ + qDebug() << "Wireless networking" << (enabled ? "enabled" : "disabled"); + m_wirelessEnabled = enabled; + emit wirelessEnabledChanged(); +} + +void NetworkManager::setConnectivityState(const NetworkManager::NetworkManagerConnectivityState &connectivityState) +{ + qDebug() << "Connectivity state changed:" << networkManagerConnectivityStateToString(connectivityState); + m_connectivityState = connectivityState; + emit connectivityStateChanged(); +} + +void NetworkManager::setState(const NetworkManager::NetworkManagerState &state) +{ + qDebug() << "State changed:" << networkManagerStateToString(state); + m_state = state; + emit stateChanged(); +} + +void NetworkManager::onDeviceAdded(const QDBusObjectPath &deviceObjectPath) +{ + if (m_networkDevices.keys().contains(deviceObjectPath)) { + qWarning() << "Device" << deviceObjectPath.path() << "already added."; + return; + } + + // Get device Type + QDBusInterface networkDeviceInterface(networkManagerServiceString, deviceObjectPath.path(), deviceInterfaceString, QDBusConnection::systemBus()); + if(!networkDeviceInterface.isValid()) { + qWarning() << "NetworkDevice: Invalid DBus device interface" << deviceObjectPath.path(); + return; + } + + // Create object + NetworkDevice::NetworkDeviceType deviceType = NetworkDevice::NetworkDeviceType(networkDeviceInterface.property("DeviceType").toUInt()); + switch (deviceType) { + case NetworkDevice::NetworkDeviceTypeWifi: { + WirelessNetworkDevice *wirelessNetworkDevice = new WirelessNetworkDevice(deviceObjectPath, this); + qDebug() << "[+]" << wirelessNetworkDevice; + m_networkDevices.insert(deviceObjectPath, wirelessNetworkDevice); + m_wirelessNetworkDevices.insert(deviceObjectPath, wirelessNetworkDevice); + connect(wirelessNetworkDevice, &WirelessNetworkDevice::deviceChanged, this, &NetworkManager::onWirelessDeviceChanged); + emit wirelessDeviceAdded(wirelessNetworkDevice); + break; + } + case NetworkDevice::NetworkDeviceTypeEthernet: { + WiredNetworkDevice *wiredNetworkDevice = new WiredNetworkDevice(deviceObjectPath, this); + qDebug() << "[+]" << wiredNetworkDevice; + m_networkDevices.insert(deviceObjectPath, wiredNetworkDevice); + m_wiredNetworkDevices.insert(deviceObjectPath, wiredNetworkDevice); + + connect(wiredNetworkDevice, &WiredNetworkDevice::deviceChanged, this, &NetworkManager::onWiredDeviceChanged); + emit wiredDeviceAdded(wiredNetworkDevice); + break; + } + default: + NetworkDevice *networkDevice = new NetworkDevice(deviceObjectPath, this); + qDebug() << "[+]" << networkDevice; + m_networkDevices.insert(deviceObjectPath, networkDevice); + break; + } +} + +void NetworkManager::onDeviceRemoved(const QDBusObjectPath &deviceObjectPath) +{ + if (!m_networkDevices.keys().contains(deviceObjectPath)) { + qWarning() << "Unknown network device removed:" << deviceObjectPath.path(); + return; + } + + NetworkDevice *networkDevice = m_networkDevices.take(deviceObjectPath); + + if (m_wiredNetworkDevices.contains(deviceObjectPath)) { + qDebug() << "[-]" << m_wiredNetworkDevices.value(deviceObjectPath); + m_wiredNetworkDevices.remove(deviceObjectPath); + emit wiredDeviceRemoved(networkDevice->interface()); + } else if (m_wirelessNetworkDevices.contains(deviceObjectPath)) { + qDebug() << "[-]" << m_wirelessNetworkDevices.value(deviceObjectPath); + m_wirelessNetworkDevices.remove(deviceObjectPath); + if (!wirelessAvailable()) + emit wirelessAvailableChanged(); + + emit wirelessDeviceRemoved(networkDevice->interface()); + } else { + qDebug() << "[-]" << networkDevice; + } + + networkDevice->deleteLater(); +} + +void NetworkManager::onPropertiesChanged(const QVariantMap &properties) +{ + if (properties.contains("Version")) + setVersion(properties.value("Version").toString()); + + if (properties.contains("State")) + setState((NetworkManagerState)properties.value("State").toUInt()); + + if (properties.contains("Connectivity")) + setConnectivityState(NetworkManagerConnectivityState(properties.value("Connectivity").toUInt())); + + if (properties.contains("NetworkingEnabled")) + setNetworkingEnabled(properties.value("NetworkingEnabled").toBool()); + + if (properties.contains("WirelessEnabled")) + setWirelessEnabled(properties.value("WirelessEnabled").toBool()); + +} + +void NetworkManager::onWirelessDeviceChanged() +{ + WirelessNetworkDevice *networkDevice = static_cast(sender()); + emit wirelessDeviceChanged(networkDevice); +} + +void NetworkManager::onWiredDeviceChanged() +{ + WiredNetworkDevice *networkDevice = static_cast(sender()); + emit wiredDeviceChanged(networkDevice); +} + diff --git a/libnymea-networkmanager/networkmanager.h b/libnymea-networkmanager/networkmanager.h new file mode 100644 index 0000000..4b637e3 --- /dev/null +++ b/libnymea-networkmanager/networkmanager.h @@ -0,0 +1,151 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of loopd. * + * * + * Loopd can not be copied and/or distributed without the express * + * permission of guh GmbH. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef NETWORKMANAGER_H +#define NETWORKMANAGER_H + +#include +#include +#include +#include +#include +#include + +#include "dbus-interfaces.h" +#include "wirednetworkdevice.h" +#include "wirelessnetworkdevice.h" +#include "networksettings.h" + +// Docs: https://developer.gnome.org/NetworkManager/unstable/spec.html + +class NetworkManager : public QObject +{ + Q_OBJECT + Q_ENUMS(NetworkManagerState) + Q_ENUMS(NetworkManagerConnectivityState) + Q_ENUMS(NetworkManagerError) + +public: + enum NetworkManagerState { + NetworkManagerStateUnknown = 0, + NetworkManagerStateAsleep = 10, + NetworkManagerStateDisconnected = 20, + NetworkManagerStateDisconnecting = 30, + NetworkManagerStateConnecting = 40, + NetworkManagerStateConnectedLocal = 50, + NetworkManagerStateConnectedSite = 60, + NetworkManagerStateConnectedGlobal = 70 + }; + + enum NetworkManagerConnectivityState { + NetworkManagerConnectivityStateUnknown = 1, + NetworkManagerConnectivityStateNone = 2, + NetworkManagerConnectivityStatePortal = 3, + NetworkManagerConnectivityStateLimited = 4, + NetworkManagerConnectivityStateFull = 5 + }; + + enum NetworkManagerError { + NetworkManagerErrorNoError, + NetworkManagerErrorUnknownError, + NetworkManagerErrorWirelessNotAvailable, + NetworkManagerErrorAccessPointNotFound, + NetworkManagerErrorNetworkInterfaceNotFound, + NetworkManagerErrorInvalidNetworkDeviceType, + NetworkManagerErrorWirelessNetworkingDisabled, + NetworkManagerErrorWirelessConnectionFailed, + NetworkManagerErrorNetworkingDisabled, + NetworkManagerErrorNetworkManagerNotAvailable + }; + + explicit NetworkManager(QObject *parent = 0); + ~NetworkManager(); + + bool available(); + bool wirelessAvailable(); + + QList networkDevices() const; + QList wirelessNetworkDevices() const; + QList wiredNetworkDevices() const; + + NetworkDevice *getNetworkDevice(const QString &interface); + + // Properties + QString version() const; + NetworkManagerState state() const; + QString stateString() const; + NetworkManagerConnectivityState connectivityState() const; + + NetworkManagerError connectWifi(const QString &interface, const QString &ssid, const QString &password, const bool &hidden = false); + + // Networking + bool networkingEnabled() const; + bool enableNetworking(const bool &enabled); + + // Wireless Networking + bool wirelessEnabled() const; + bool enableWireless(const bool &enabled); + +private: + QDBusInterface *m_networkManagerInterface; + + QHash m_networkDevices; + QHash m_wirelessNetworkDevices; + QHash m_wiredNetworkDevices; + + NetworkSettings *m_networkSettings; + + bool m_available; + + QString m_version; + + NetworkManagerState m_state; + NetworkManagerConnectivityState m_connectivityState; + bool m_networkingEnabled; + bool m_wirelessEnabled; + + void loadDevices(); + + static QString networkManagerStateToString(const NetworkManagerState &state); + static QString networkManagerConnectivityStateToString(const NetworkManagerConnectivityState &state); + + void setVersion(const QString &version); + void setNetworkingEnabled(const bool &enabled); + void setWirelessEnabled(const bool &enabled); + void setConnectivityState(const NetworkManagerConnectivityState &connectivityState); + void setState(const NetworkManagerState &state); + +signals: + void versionChanged(); + void networkingEnabledChanged(); + void wirelessEnabledChanged(); + void wirelessAvailableChanged(); + void stateChanged(); + void connectivityStateChanged(); + + void wirelessDeviceAdded(WirelessNetworkDevice *wirelessDevice); + void wirelessDeviceRemoved(const QString &interface); + void wirelessDeviceChanged(WirelessNetworkDevice *wirelessDevice); + + void wiredDeviceAdded(WiredNetworkDevice *wiredDevice); + void wiredDeviceRemoved(const QString &interface); + void wiredDeviceChanged(WiredNetworkDevice *wiredDevice); + +private slots: + void onDeviceAdded(const QDBusObjectPath &deviceObjectPath); + void onDeviceRemoved(const QDBusObjectPath &deviceObjectPath); + void onPropertiesChanged(const QVariantMap &properties); + + void onWirelessDeviceChanged(); + void onWiredDeviceChanged(); +}; + +#endif // NETWORKMANAGER_H diff --git a/libnymea-networkmanager/networksettings.cpp b/libnymea-networkmanager/networksettings.cpp new file mode 100644 index 0000000..be65008 --- /dev/null +++ b/libnymea-networkmanager/networksettings.cpp @@ -0,0 +1,94 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of loopd. * + * * + * Loopd can not be copied and/or distributed without the express * + * permission of guh GmbH. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "networksettings.h" +#include "dbus-interfaces.h" + +#include + +/*! Constructs a new \l{NetworkSettings} object with the given \a parent. */ +NetworkSettings::NetworkSettings(QObject *parent) : QObject(parent) +{ + m_settingsInterface = new QDBusInterface(networkManagerServiceString, settingsPathString, settingsInterfaceString, QDBusConnection::systemBus(), this); + if(!m_settingsInterface->isValid()) { + qWarning() << "Invalid DBus network settings interface"; + return; + } + + loadConnections(); + + QDBusConnection::systemBus().connect(networkManagerServiceString, settingsPathString, settingsInterfaceString, "NewConnection", this, SLOT(connectionAdded(QDBusObjectPath))); + QDBusConnection::systemBus().connect(networkManagerServiceString, settingsPathString, settingsInterfaceString, "ConnectionRemoved", this, SLOT(connectionRemoved(QDBusObjectPath))); + QDBusConnection::systemBus().connect(networkManagerServiceString, settingsPathString, settingsInterfaceString, "PropertiesChanged", this, SLOT(propertiesChanged(QVariantMap))); +} + +/*! Add the given \a settings to this \l{NetworkSettings}. Returns the dbus object path from the new settings. */ +QDBusObjectPath NetworkSettings::addConnection(const ConnectionSettings &settings) +{ + QDBusMessage query = m_settingsInterface->call("AddConnection", QVariant::fromValue(settings)); + if(query.type() != QDBusMessage::ReplyMessage) { + qWarning() << query.errorName() << query.errorMessage(); + return QDBusObjectPath(); + } + + if (query.arguments().isEmpty()) + return QDBusObjectPath(); + + return query.arguments().at(0).value(); +} + +/*! Returns the list of current \l{NetworkConnection}{NetworkConnections} from this \l{NetworkSettings}. */ +QList NetworkSettings::connections() const +{ + return m_connections.values(); +} + +void NetworkSettings::loadConnections() +{ + QDBusMessage query = m_settingsInterface->call("ListConnections"); + if(query.type() != QDBusMessage::ReplyMessage) { + qWarning() << query.errorName() << query.errorMessage(); + return; + } + + if (query.arguments().isEmpty()) + return; + + const QDBusArgument &argument = query.arguments().at(0).value(); + argument.beginArray(); + while(!argument.atEnd()) { + QDBusObjectPath objectPath = qdbus_cast(argument); + connectionAdded(objectPath); + } + argument.endArray(); + +} + +void NetworkSettings::connectionAdded(const QDBusObjectPath &objectPath) +{ + NetworkConnection *connection = new NetworkConnection(objectPath, this); + m_connections.insert(objectPath, connection); + + qDebug() << "Settings: [+]" << connection; +} + +void NetworkSettings::connectionRemoved(const QDBusObjectPath &objectPath) +{ + NetworkConnection *connection = m_connections.take(objectPath); + qDebug() << "Settings: [-]" << connection; + connection->deleteLater(); +} + +void NetworkSettings::propertiesChanged(const QVariantMap &properties) +{ + Q_UNUSED(properties); + //qDebug() << "Settins: properties changed" << properties; +} diff --git a/libnymea-networkmanager/networksettings.h b/libnymea-networkmanager/networksettings.h new file mode 100644 index 0000000..e7c08f8 --- /dev/null +++ b/libnymea-networkmanager/networksettings.h @@ -0,0 +1,49 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of loopd. * + * * + * Loopd can not be copied and/or distributed without the express * + * permission of guh GmbH. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef NETWORKSETTINGS_H +#define NETWORKSETTINGS_H + +#include +#include +#include +#include +#include + +#include "networkconnection.h" + +class NetworkConnection; + +class NetworkSettings : public QObject +{ + Q_OBJECT +public: + explicit NetworkSettings(QObject *parent = 0); + + QDBusObjectPath addConnection(const ConnectionSettings &settings); + QList connections() const; + +private: + QDBusInterface *m_settingsInterface; + QHash m_connections; + + void loadConnections(); + +signals: + +private slots: + void connectionAdded(const QDBusObjectPath &objectPath); + void connectionRemoved(const QDBusObjectPath &objectPath); + void propertiesChanged(const QVariantMap &properties); + +}; + +#endif // NETWORKSETTINGS_H diff --git a/libnymea-networkmanager/wirednetworkdevice.cpp b/libnymea-networkmanager/wirednetworkdevice.cpp new file mode 100644 index 0000000..2233446 --- /dev/null +++ b/libnymea-networkmanager/wirednetworkdevice.cpp @@ -0,0 +1,87 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Simon Stürz * + * * + * This file is part of loopd. * + * * + * Loopd can not be copied and/or distributed without the express * + * permission of guh GmbH. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "wirednetworkdevice.h" + +#include + +/*! Constructs a new \l{WiredNetworkDevice} with the given dbus \a objectPath and \a parent. */ +WiredNetworkDevice::WiredNetworkDevice(const QDBusObjectPath &objectPath, QObject *parent) : + NetworkDevice(objectPath, parent) +{ + QDBusConnection systemBus = QDBusConnection::systemBus(); + if (!systemBus.isConnected()) { + qWarning() << "WiredNetworkDevice: System DBus not connected"; + return; + } + + m_wiredInterface = new QDBusInterface(networkManagerServiceString, this->objectPath().path(), wiredInterfaceString, systemBus, this); + if(!m_wiredInterface->isValid()) { + qWarning() << "WiredNetworkDevice: Invalid wired dbus interface"; + return; + } + + setMacAddress(m_wiredInterface->property("HwAddress").toString()); + setBitRate(m_wiredInterface->property("Bitrate").toInt()); + setPluggedIn(m_wiredInterface->property("Carrier").toBool()); + + QDBusConnection::systemBus().connect(networkManagerServiceString, this->objectPath().path(), wiredInterfaceString, "PropertiesChanged", this, SLOT(propertiesChanged(QVariantMap))); +} + +/*! Returns the mac address of this \l{WiredNetworkDevice}. */ +QString WiredNetworkDevice::macAddress() const +{ + return m_macAddress; +} + +/*! Returns the current bit rate [Mb/s] of this \l{WiredNetworkDevice}. */ +int WiredNetworkDevice::bitRate() const +{ + return m_bitRate; +} + +/*! Returns true if this \l{WiredNetworkDevice} has a cable plugged in. */ +bool WiredNetworkDevice::pluggedIn() const +{ + return m_pluggedIn; +} + +void WiredNetworkDevice::setMacAddress(const QString &macAddress) +{ + m_macAddress = macAddress; +} + +void WiredNetworkDevice::setBitRate(const int &bitRate) +{ + m_bitRate = bitRate; +} + +void WiredNetworkDevice::setPluggedIn(const bool &pluggedIn) +{ + m_pluggedIn = pluggedIn; +} + +void WiredNetworkDevice::propertiesChanged(const QVariantMap &properties) +{ + if (properties.contains("Carrier")) + setPluggedIn(properties.value("Carrier").toBool()); + +} + +QDebug operator<<(QDebug debug, WiredNetworkDevice *networkDevice) +{ + debug.nospace() << "WiredNetworkDevice(" << networkDevice->interface() << ", "; + debug.nospace() << networkDevice->macAddress() << ", "; + debug.nospace() << networkDevice->bitRate() << " [Mb/s], "; + debug.nospace() << networkDevice->pluggedIn() << ", "; + debug.nospace() << networkDevice->deviceStateString() << ") "; + return debug; +} diff --git a/libnymea-networkmanager/wirednetworkdevice.h b/libnymea-networkmanager/wirednetworkdevice.h new file mode 100644 index 0000000..0570333 --- /dev/null +++ b/libnymea-networkmanager/wirednetworkdevice.h @@ -0,0 +1,48 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of loopd. * + * * + * Loopd can not be copied and/or distributed without the express * + * permission of guh GmbH. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef WIREDNETWORKDEVICE_H +#define WIREDNETWORKDEVICE_H + +#include +#include + +#include "networkdevice.h" + +class WiredNetworkDevice : public NetworkDevice +{ + Q_OBJECT +public: + explicit WiredNetworkDevice(const QDBusObjectPath &objectPath, QObject *parent = 0); + + QString macAddress() const; + int bitRate() const; + bool pluggedIn() const; + +private: + QDBusInterface *m_wiredInterface; + + QString m_macAddress; + int m_bitRate; + bool m_pluggedIn; + + void setMacAddress(const QString &macAddress); + void setBitRate(const int &bitRate); + void setPluggedIn(const bool &pluggedIn); + +private slots: + void propertiesChanged(const QVariantMap &properties); + +}; + +QDebug operator<<(QDebug debug, WiredNetworkDevice *networkDevice); + +#endif // WIREDNETWORKDEVICE_H diff --git a/libnymea-networkmanager/wirelessaccesspoint.cpp b/libnymea-networkmanager/wirelessaccesspoint.cpp new file mode 100644 index 0000000..442a327 --- /dev/null +++ b/libnymea-networkmanager/wirelessaccesspoint.cpp @@ -0,0 +1,141 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of loopd. * + * * + * Loopd can not be copied and/or distributed without the express * + * permission of guh GmbH. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/*! \enum guhserver::WirelessAccessPoint::ApSecurityMode + \value ApSecurityModeNone + \value ApSecurityModePairWep40 + \value ApSecurityModePairWep104 + \value ApSecurityModePairTkip + \value ApSecurityModePairCcmp + \value ApSecurityModeGroupWep40 + \value ApSecurityModeGroupWep104 + \value ApSecurityModeGroupTkip + \value ApSecurityModeGroupCcmp + \value ApSecurityModeKeyMgmtPsk + \value ApSecurityModeKeyMgmt8021X +*/ + +/*! \fn void WirelessAccessPoint::signalStrengthChanged(); + This signal will be emitted when the signalStrength of this \l{WirelessAccessPoint} has changed. +*/ + +#include "wirelessaccesspoint.h" +#include "dbus-interfaces.h" + +#include + +/*! Constructs a new \l{WirelessAccessPoint} with the given dbus \a objectPath and \a parent. */ +WirelessAccessPoint::WirelessAccessPoint(const QDBusObjectPath &objectPath, QObject *parent) : + QObject(parent), + m_objectPath(objectPath), + m_securityFlags(0) +{ + QDBusInterface accessPointInterface(networkManagerServiceString, m_objectPath.path(), accessPointInterfaceString, QDBusConnection::systemBus()); + if(!accessPointInterface.isValid()) { + qWarning() << "Invalid access point dbus interface"; + return; + } + + // Init properties + setSsid(accessPointInterface.property("Ssid").toString()); + setMacAddress(accessPointInterface.property("HwAddress").toString()); + setFrequency(accessPointInterface.property("Frequency").toDouble() / 1000); + setSignalStrength(accessPointInterface.property("Strength").toUInt()); + setSecurityFlags(WirelessAccessPoint::ApSecurityModes(accessPointInterface.property("WpaFlags").toUInt())); + setIsProtected((bool)accessPointInterface.property("Flags").toUInt()); + + QDBusConnection::systemBus().connect(networkManagerServiceString, objectPath.path(), accessPointInterfaceString, "PropertiesChanged", this, SLOT(onPropertiesChanged(QVariantMap))); +} + +/*! Returns the dbus object path of this \l{WirelessAccessPoint}. */ +QDBusObjectPath WirelessAccessPoint::objectPath() const +{ + return m_objectPath; +} + +/*! Returns the ssid of this \l{WirelessAccessPoint}. */ +QString WirelessAccessPoint::ssid() const +{ + return m_ssid; +} + +void WirelessAccessPoint::setSsid(const QString &ssid) +{ + m_ssid = ssid; +} + +/*! Returns the mac address of this \l{WirelessAccessPoint}. */ +QString WirelessAccessPoint::macAddress() const +{ + return m_macAddress; +} + +void WirelessAccessPoint::setMacAddress(const QString &macAddress) +{ + m_macAddress = macAddress; +} + +/*! Returns the frequency of this \l{WirelessAccessPoint}. (2.4 GHz or 5GHz) */ +double WirelessAccessPoint::frequency() const +{ + return m_frequency; +} + +void WirelessAccessPoint::setFrequency(const double &frequency) +{ + m_frequency = frequency; +} + +/*! Returns the signal strength in percentage [0, 100] % of this \l{WirelessAccessPoint}. */ +int WirelessAccessPoint::signalStrength() const +{ + return m_signalStrength; +} + +void WirelessAccessPoint::setSignalStrength(const int &signalStrength) +{ + m_signalStrength = signalStrength; + emit signalStrengthChanged(); +} + +void WirelessAccessPoint::setIsProtected(const bool &isProtected) +{ + m_isProtected = isProtected; +} + +/*! Returns true if this \l{WirelessAccessPoint} is password protected. */ +bool WirelessAccessPoint::isProtected() const +{ + return m_isProtected; +} + +/*! Returns the security flags of this \l{WirelessAccessPoint}. \sa WirelessAccessPoint::ApSecurityModes */ +WirelessAccessPoint::ApSecurityModes WirelessAccessPoint::securityFlags() const +{ + return m_securityFlags; +} + +void WirelessAccessPoint::setSecurityFlags(const WirelessAccessPoint::ApSecurityModes &securityFlags) +{ + m_securityFlags = securityFlags; +} + +void WirelessAccessPoint::onPropertiesChanged(const QVariantMap &properties) +{ + if (properties.contains("Strength")) + setSignalStrength(properties.value("Strength").toUInt()); + +} + +QDebug operator<<(QDebug debug, WirelessAccessPoint *accessPoint) +{ + return debug.nospace() << "AccessPoint(" << accessPoint->signalStrength() << "%, " << accessPoint->frequency()<< " GHz, " << accessPoint->ssid() << ", " << (accessPoint->isProtected() ? "protected" : "open" ) << ")"; +} diff --git a/libnymea-networkmanager/wirelessaccesspoint.h b/libnymea-networkmanager/wirelessaccesspoint.h new file mode 100644 index 0000000..075f427 --- /dev/null +++ b/libnymea-networkmanager/wirelessaccesspoint.h @@ -0,0 +1,83 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of loopd. * + * * + * Loopd can not be copied and/or distributed without the express * + * permission of guh GmbH. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef WIRELESSACCESSPOINT_H +#define WIRELESSACCESSPOINT_H + +#include +#include +#include +#include +#include +#include +#include + +class WirelessAccessPoint : public QObject +{ + Q_OBJECT + Q_FLAGS(ApSecurityModes) + +public: + enum ApSecurityMode{ + ApSecurityModeNone = 0x000, + ApSecurityModePairWep40 = 0x001, + ApSecurityModePairWep104 = 0x002, + ApSecurityModePairTkip = 0x004, + ApSecurityModePairCcmp = 0x008, + ApSecurityModeGroupWep40 = 0x010, + ApSecurityModeGroupWep104 = 0x020, + ApSecurityModeGroupTkip = 0x040, + ApSecurityModeGroupCcmp = 0x080, + ApSecurityModeKeyMgmtPsk = 0x100, + ApSecurityModeKeyMgmt8021X = 0x200, + }; + Q_DECLARE_FLAGS(ApSecurityModes, ApSecurityMode) + + + explicit WirelessAccessPoint(const QDBusObjectPath &objectPath, QObject *parent = 0); + + QDBusObjectPath objectPath() const; + + QString ssid() const; + QString macAddress() const; + double frequency() const; + int signalStrength() const; + bool isProtected() const; + + WirelessAccessPoint::ApSecurityModes securityFlags() const; + +private: + QDBusObjectPath m_objectPath; + QString m_ssid; + QString m_macAddress; + double m_frequency; + int m_signalStrength; + bool m_isProtected; + WirelessAccessPoint::ApSecurityModes m_securityFlags; + + void setSsid(const QString &ssid); + void setMacAddress(const QString &macAddress); + void setFrequency(const double &frequency); + void setSignalStrength(const int &signalStrength); + void setIsProtected(const bool &isProtected); + void setSecurityFlags(const WirelessAccessPoint::ApSecurityModes &securityFlags); + +signals: + void signalStrengthChanged(); + +private slots: + void onPropertiesChanged(const QVariantMap &properties); + +}; + +QDebug operator<<(QDebug debug, WirelessAccessPoint *accessPoint); + +#endif // WIRELESSACCESSPOINT_H diff --git a/libnymea-networkmanager/wirelessnetworkdevice.cpp b/libnymea-networkmanager/wirelessnetworkdevice.cpp new file mode 100644 index 0000000..114b0b1 --- /dev/null +++ b/libnymea-networkmanager/wirelessnetworkdevice.cpp @@ -0,0 +1,211 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of loopd. * + * * + * Loopd can not be copied and/or distributed without the express * + * permission of guh GmbH. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/*! \fn void WirelessNetworkDevice::bitRateChanged(const int &bitRate); + This signal will be emitted when the \a bitRate of this \l{WirelessNetworkDevice} has changed. +*/ + +/*! \fn void WirelessNetworkDevice::stateChanged(const NetworkDeviceState &state); + This signal will be emitted when the current \a state of this \l{WirelessNetworkDevice} has changed. + + \sa NetworkDeviceState +*/ + + +#include "dbus-interfaces.h" +#include "wirelessnetworkdevice.h" + +#include +#include +#include + +/*! Constructs a new \l{WirelessNetworkDevice} with the given dbus \a objectPath and \a parent. */ +WirelessNetworkDevice::WirelessNetworkDevice(const QDBusObjectPath &objectPath, QObject *parent) : + NetworkDevice(objectPath, parent), + m_activeAccessPoint(Q_NULLPTR) +{ + QDBusConnection systemBus = QDBusConnection::systemBus(); + if (!systemBus.isConnected()) { + qWarning() << "WirelessNetworkDevice: System DBus not connected"; + return; + } + + m_wirelessInterface = new QDBusInterface(networkManagerServiceString, this->objectPath().path(), wirelessInterfaceString, systemBus, this); + if (!m_wirelessInterface->isValid()) { + qWarning() << "WirelessNetworkDevice: Invalid wireless dbus interface"; + return; + } + + QDBusConnection::systemBus().connect(networkManagerServiceString, this->objectPath().path(), wirelessInterfaceString, "AccessPointAdded", this, SLOT(accessPointAdded(QDBusObjectPath))); + QDBusConnection::systemBus().connect(networkManagerServiceString, this->objectPath().path(), wirelessInterfaceString, "AccessPointRemoved", this, SLOT(accessPointRemoved(QDBusObjectPath))); + QDBusConnection::systemBus().connect(networkManagerServiceString, this->objectPath().path(), wirelessInterfaceString, "PropertiesChanged", this, SLOT(propertiesChanged(QVariantMap))); + + readAccessPoints(); + + setMacAddress(m_wirelessInterface->property("HwAddress").toString()); + setBitrate(m_wirelessInterface->property("Bitrate").toInt()); + setActiveAccessPoint(qdbus_cast(m_wirelessInterface->property("ActiveAccessPoint"))); +} + +/*! Returns the mac address of this \l{WirelessNetworkDevice}. */ +QString WirelessNetworkDevice::macAddress() const +{ + return m_macAddress; +} + +/*! Returns the bit rate [Mb/s] of this \l{WirelessNetworkDevice}. */ +int WirelessNetworkDevice::bitRate() const +{ + return m_bitRate; +} + +/*! Returns the current active \l{WirelessAccessPoint} of this \l{WirelessNetworkDevice}. */ +WirelessAccessPoint *WirelessNetworkDevice::activeAccessPoint() +{ + return m_activeAccessPoint; +} + +/*! Perform a wireless network scan on this \l{WirelessNetworkDevice}. */ +void WirelessNetworkDevice::scanWirelessNetworks() +{ + qDebug() << this << "Request scan"; + QDBusMessage query = m_wirelessInterface->call("RequestScan", QVariantMap()); + if (query.type() != QDBusMessage::ReplyMessage) { + qWarning() << "Scan error:" << query.errorName() << query.errorMessage(); + return; + } +} + +/*! Returns the list of all \l{WirelessAccessPoint}{WirelessAccessPoints} of this \l{WirelessNetworkDevice}. */ +QList WirelessNetworkDevice::accessPoints() +{ + return m_accessPointsTable.values(); +} + +/*! Returns the \l{WirelessAccessPoint} with the given \a ssid. If the \l{WirelessAccessPoint} could not be found, return Q_NULLPTR. */ +WirelessAccessPoint *WirelessNetworkDevice::getAccessPoint(const QString &ssid) +{ + foreach (WirelessAccessPoint *accessPoint, m_accessPointsTable.values()) { + if (accessPoint->ssid() == ssid) + return accessPoint; + } + return Q_NULLPTR; +} + +/*! Returns the \l{WirelessAccessPoint} with the given \a objectPath. If the \l{WirelessAccessPoint} could not be found, return Q_NULLPTR. */ +WirelessAccessPoint *WirelessNetworkDevice::getAccessPoint(const QDBusObjectPath &objectPath) +{ + return m_accessPointsTable.value(objectPath); +} + +void WirelessNetworkDevice::readAccessPoints() +{ + QDBusMessage query = m_wirelessInterface->call("GetAccessPoints"); + if(query.type() != QDBusMessage::ReplyMessage) { + qWarning() << query.errorName() << query.errorMessage(); + return; + } + + if (query.arguments().isEmpty()) + return; + + const QDBusArgument &argument = query.arguments().at(0).value(); + argument.beginArray(); + while (!argument.atEnd()) { + QDBusObjectPath accessPointObjectPath = qdbus_cast(argument); + accessPointAdded(accessPointObjectPath); + } + argument.endArray(); +} + +void WirelessNetworkDevice::setMacAddress(const QString &macAddress) +{ + m_macAddress = macAddress; +} + +void WirelessNetworkDevice::setBitrate(const int &bitRate) +{ + if (m_bitRate != bitRate / 1000) { + m_bitRate = bitRate / 1000; + emit deviceChanged(); + } +} + +void WirelessNetworkDevice::setActiveAccessPoint(const QDBusObjectPath &activeAccessPointObjectPath) +{ + if (m_activeAccessPointObjectPath != activeAccessPointObjectPath) { + m_activeAccessPointObjectPath = activeAccessPointObjectPath; + if (m_accessPointsTable.contains(m_activeAccessPointObjectPath)) { + if (m_activeAccessPoint) + disconnect(m_activeAccessPoint, &WirelessAccessPoint::signalStrengthChanged, this, &WirelessNetworkDevice::deviceChanged); + + // Set new access point object + m_activeAccessPoint = m_accessPointsTable.value(activeAccessPointObjectPath); + // Update the device when the signalstrength changed + connect(m_activeAccessPoint, &WirelessAccessPoint::signalStrengthChanged, this, &WirelessNetworkDevice::deviceChanged); + } else { + m_activeAccessPoint = Q_NULLPTR; + } + emit deviceChanged(); + } +} + +void WirelessNetworkDevice::accessPointAdded(const QDBusObjectPath &objectPath) +{ + QDBusInterface accessPointInterface(networkManagerServiceString, objectPath.path(), accessPointInterfaceString, QDBusConnection::systemBus()); + if (!accessPointInterface.isValid()) { + qWarning() << "WirelessNetworkDevice: Invalid access point dbus interface"; + return; + } + + if (m_accessPointsTable.keys().contains(objectPath)) { + qWarning() << "WirelessNetworkDevice: Access point already added" << objectPath.path(); + return; + } + + WirelessAccessPoint *accessPoint = new WirelessAccessPoint(objectPath, this); + //qDebug() << "WirelessNetworkDevice: [+]" << accessPoint; + m_accessPointsTable.insert(objectPath, accessPoint); +} + +void WirelessNetworkDevice::accessPointRemoved(const QDBusObjectPath &objectPath) +{ + if (!m_accessPointsTable.keys().contains(objectPath)) + return; + + WirelessAccessPoint *accessPoint = m_accessPointsTable.take(objectPath); + if (accessPoint == m_activeAccessPoint) + m_activeAccessPoint = Q_NULLPTR; + + //qDebug() << "WirelessNetworkDevice: [-]" << accessPoint; + accessPoint->deleteLater(); +} + +void WirelessNetworkDevice::propertiesChanged(const QVariantMap &properties) +{ + //qDebug() << "WirelessNetworkDevice: Property changed" << properties; + + if (properties.contains("Bitrate")) + setBitrate(properties.value("Bitrate").toInt()); + + if (properties.contains("ActiveAccessPoint")) + setActiveAccessPoint(qdbus_cast(properties.value("ActiveAccessPoint"))); +} + +/*! Writes the given \a device to the given to \a debug. \sa WirelessNetworkDevice, */ +QDebug operator<<(QDebug debug, WirelessNetworkDevice *device) +{ + debug.nospace() << "WirelessNetworkDevice(" << device->interface() << ", "; + debug.nospace() << device->macAddress() << ", "; + debug.nospace() << device->bitRate() << " [Mb/s], "; + debug.nospace() << device->deviceStateString() << ") "; + return debug; +} diff --git a/libnymea-networkmanager/wirelessnetworkdevice.h b/libnymea-networkmanager/wirelessnetworkdevice.h new file mode 100644 index 0000000..5732828 --- /dev/null +++ b/libnymea-networkmanager/wirelessnetworkdevice.h @@ -0,0 +1,74 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of loopd. * + * * + * Loopd can not be copied and/or distributed without the express * + * permission of guh GmbH. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef WIRELESSNETWORKMANAGER_H +#define WIRELESSNETWORKMANAGER_H + +#include +#include +#include +#include +#include +#include +#include + +#include "networkdevice.h" +#include "wirelessaccesspoint.h" + +class WirelessNetworkDevice : public NetworkDevice +{ + Q_OBJECT +public: + + explicit WirelessNetworkDevice(const QDBusObjectPath &objectPath, QObject *parent = 0); + + // Properties + QString macAddress() const; + int bitRate() const; + WirelessAccessPoint *activeAccessPoint(); + + // Accesspoints + QList accessPoints(); + WirelessAccessPoint *getAccessPoint(const QString &ssid); + WirelessAccessPoint *getAccessPoint(const QDBusObjectPath &objectPath); + + // Methods + void scanWirelessNetworks(); + +private: + QDBusInterface *m_wirelessInterface; + + QString m_macAddress; + int m_bitRate; + WirelessAccessPoint *m_activeAccessPoint; + QDBusObjectPath m_activeAccessPointObjectPath; + + QHash m_accessPointsTable; + + void readAccessPoints(); + + void setMacAddress(const QString &macAddress); + void setBitrate(const int &bitRate); + void setActiveAccessPoint(const QDBusObjectPath &activeAccessPointObjectPath); + +private slots: + void accessPointAdded(const QDBusObjectPath &objectPath); + void accessPointRemoved(const QDBusObjectPath &objectPath); + void propertiesChanged(const QVariantMap &properties); + +signals: + void bitRateChanged(const int &bitRate); + +}; + +QDebug operator<<(QDebug debug, WirelessNetworkDevice *device); + +#endif // WIRELESSNETWORKMANAGER_H diff --git a/nymea-networkmanager.pri b/nymea-networkmanager.pri new file mode 100644 index 0000000..f2730f7 --- /dev/null +++ b/nymea-networkmanager.pri @@ -0,0 +1,6 @@ +QMAKE_CXXFLAGS *= -Werror -std=c++1z -g +QMAKE_LFLAGS *= -std=c++1z + +top_srcdir=$$PWD +top_builddir=$$shadowed($$PWD) + diff --git a/nymea-networkmanager.pro b/nymea-networkmanager.pro new file mode 100644 index 0000000..e108bce --- /dev/null +++ b/nymea-networkmanager.pro @@ -0,0 +1,5 @@ +TEMPLATE=subdirs + +networkmanager.depends = libnymea-networkmanager + +SUBDIRS += libnymea-networkmanager nymea-networkmanager diff --git a/nymea-networkmanager/bluetooth/bluetoothserver.cpp b/nymea-networkmanager/bluetooth/bluetoothserver.cpp new file mode 100644 index 0000000..86872ea --- /dev/null +++ b/nymea-networkmanager/bluetooth/bluetoothserver.cpp @@ -0,0 +1,439 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of loopd. * + * * + * Loopd can not be copied and/or distributed without the express * + * permission of guh GmbH. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "bluetoothserver.h" +#include "loggingcategory.h" +#include "loopd.h" +#include "bluetoothuuids.h" + +#include +#include + +BluetoothServer::BluetoothServer(const QString &machineId, QObject *parent) : + QObject(parent), + m_machineId(machineId) +{ + m_advertisingTimer = new QTimer(this); + m_advertisingTimer->setInterval(60000); + m_advertisingTimer->setSingleShot(true); + + connect(m_advertisingTimer, &QTimer::timeout, this, &BluetoothServer::onAdvertisingTimeout); +} + +BluetoothServer::~BluetoothServer() +{ + qCDebug(dcLoopd()) << "Destroy bluetooth server."; + if (m_controller) + m_controller->stopAdvertising(); + + if (m_localDevice) + m_localDevice->setHostMode(QBluetoothLocalDevice::HostConnectable); + +} + +bool BluetoothServer::running() const +{ + return m_running; +} + +bool BluetoothServer::connected() const +{ + return m_connected; +} + +QLowEnergyServiceData BluetoothServer::deviceInformationServiceData() +{ + QLowEnergyServiceData serviceData; + serviceData.setType(QLowEnergyServiceData::ServiceTypePrimary); + serviceData.setUuid(QBluetoothUuid::DeviceInformation); + + // Model number string 0x2a24 + QLowEnergyCharacteristicData modelNumberCharData; + modelNumberCharData.setUuid(QBluetoothUuid::ModelNumberString); + if (m_machineId.isEmpty()) { + modelNumberCharData.setValue(QString("N.A.").toUtf8()); + } else { + modelNumberCharData.setValue(m_machineId.toUtf8()); + } + + modelNumberCharData.setProperties(QLowEnergyCharacteristic::Read); + serviceData.addCharacteristic(modelNumberCharData); + + // Firmware revision string 0x2a26 + QLowEnergyCharacteristicData firmwareRevisionCharData; + firmwareRevisionCharData.setUuid(QBluetoothUuid::FirmwareRevisionString); + firmwareRevisionCharData.setValue(QString("1.0.0").toUtf8()); + firmwareRevisionCharData.setProperties(QLowEnergyCharacteristic::Read); + serviceData.addCharacteristic(firmwareRevisionCharData); + + // Hardware revision string 0x2a27 + QLowEnergyCharacteristicData hardwareRevisionCharData; + hardwareRevisionCharData.setUuid(QBluetoothUuid::HardwareRevisionString); + hardwareRevisionCharData.setValue(QString("1.0.0").toUtf8()); + hardwareRevisionCharData.setProperties(QLowEnergyCharacteristic::Read); + serviceData.addCharacteristic(hardwareRevisionCharData); + +#ifdef SNAPPY + // Software revision string 0x2a28 + QLowEnergyCharacteristicData softwareRevisionCharData; + softwareRevisionCharData.setUuid(QBluetoothUuid::SoftwareRevisionString); + softwareRevisionCharData.setValue(qgetenv("SNAP_VERSION")); + softwareRevisionCharData.setProperties(QLowEnergyCharacteristic::Read); + serviceData.addCharacteristic(softwareRevisionCharData); +#else + // Software revision string 0x2a28 + QLowEnergyCharacteristicData softwareRevisionCharData; + softwareRevisionCharData.setUuid(QBluetoothUuid::SoftwareRevisionString); + softwareRevisionCharData.setValue(QCoreApplication::applicationVersion().toUtf8()); + softwareRevisionCharData.setProperties(QLowEnergyCharacteristic::Read); + serviceData.addCharacteristic(softwareRevisionCharData); +#endif + + // Manufacturer name string 0x2a29 + QLowEnergyCharacteristicData manufacturerNameCharData; + manufacturerNameCharData.setUuid(QBluetoothUuid::ManufacturerNameString); + manufacturerNameCharData.setValue(QString("guh GmbH").toUtf8()); + manufacturerNameCharData.setProperties(QLowEnergyCharacteristic::Read); + serviceData.addCharacteristic(manufacturerNameCharData); + + return serviceData; +} + +QLowEnergyServiceData BluetoothServer::genericAccessServiceData() +{ + QLowEnergyServiceData serviceData; + serviceData.setType(QLowEnergyServiceData::ServiceTypePrimary); + serviceData.setUuid(QBluetoothUuid::GenericAccess); + + // Device name 0x2a00 + QLowEnergyCharacteristicData nameCharData; + nameCharData.setUuid(QBluetoothUuid::DeviceName); + nameCharData.setValue(QString("Loop-box").toUtf8()); + nameCharData.setProperties(QLowEnergyCharacteristic::Read); + serviceData.addCharacteristic(nameCharData); + + // Appearance 0x2a01 + QLowEnergyCharacteristicData appearanceCharData; + appearanceCharData.setUuid(QBluetoothUuid::Appearance); + appearanceCharData.setValue(QByteArray(4, 0)); + appearanceCharData.setProperties(QLowEnergyCharacteristic::Read); + serviceData.addCharacteristic(appearanceCharData); + + // Peripheral Privacy Flag 0x2a02 + QLowEnergyCharacteristicData privacyFlagCharData; + privacyFlagCharData.setUuid(QBluetoothUuid::PeripheralPrivacyFlag); + privacyFlagCharData.setValue(QByteArray(2, 0)); + privacyFlagCharData.setProperties(QLowEnergyCharacteristic::Read | QLowEnergyCharacteristic::Write); + serviceData.addCharacteristic(privacyFlagCharData); + + // Reconnection Address 0x2a03 + QLowEnergyCharacteristicData reconnectionAddressCharData; + reconnectionAddressCharData.setUuid(QBluetoothUuid::ReconnectionAddress); + reconnectionAddressCharData.setValue(QByteArray()); + reconnectionAddressCharData.setProperties(QLowEnergyCharacteristic::Write); + serviceData.addCharacteristic(reconnectionAddressCharData); + + return serviceData; +} + +QLowEnergyServiceData BluetoothServer::genericAttributeServiceData() +{ + QLowEnergyServiceData serviceData; + serviceData.setType(QLowEnergyServiceData::ServiceTypePrimary); + serviceData.setUuid(QBluetoothUuid::GenericAttribute); + + QLowEnergyCharacteristicData charData; + charData.setUuid(QBluetoothUuid::ServiceChanged); + charData.setProperties(QLowEnergyCharacteristic::Indicate); + + serviceData.addCharacteristic(charData); + + return serviceData; +} + +void BluetoothServer::setRunning(const bool &running) +{ + if (m_running == running) + return; + + m_running = running; + emit runningChanged(m_running); +} + +void BluetoothServer::setConnected(const bool &connected) +{ + if (m_connected == connected) + return; + + m_connected = connected; + emit connectedChanged(m_connected); +} + +void BluetoothServer::onHostModeStateChanged(const QBluetoothLocalDevice::HostMode mode) +{ + switch (mode) { + case QBluetoothLocalDevice::HostConnectable: + qCDebug(dcBluetoothServer()) << "Bluetooth host in connectable mode."; + break; + case QBluetoothLocalDevice::HostDiscoverable: + qCDebug(dcBluetoothServer()) << "Bluetooth host in discoverable mode."; + break; + case QBluetoothLocalDevice::HostPoweredOff: + qCDebug(dcBluetoothServer()) << "Bluetooth host in power off mode."; + break; + case QBluetoothLocalDevice::HostDiscoverableLimitedInquiry: + qCDebug(dcBluetoothServer()) << "Bluetooth host in discoverable limited inquiry mode."; + break; + default: + break; + } +} + +void BluetoothServer::onDeviceConnected(const QBluetoothAddress &address) +{ + qCDebug(dcBluetoothServer()) << "Device connected" << address.toString(); +} + +void BluetoothServer::onDeviceDisconnected(const QBluetoothAddress &address) +{ + qCDebug(dcBluetoothServer()) << "Device disconnected" << address.toString(); +} + +void BluetoothServer::onError(const QLowEnergyController::Error &error) +{ + qCWarning(dcBluetoothServer()) << "Bluetooth error occured:" << error << m_controller->errorString(); + if (error == QLowEnergyController::AdvertisingError) { + qCWarning(dcBluetoothServer()) << "Advertising error: enable error mode"; + Loopd::instance()->hardwareManager()->ledController()->setState(LedController::LedStateBlinkingFast); + } +} + +void BluetoothServer::onConnected() +{ + qCDebug(dcBluetoothServer()) << "Client connected" << m_controller->remoteName() << m_controller->remoteAddress(); + setConnected(true); +} + +void BluetoothServer::onDisconnected() +{ + qCDebug(dcBluetoothServer()) << "Client disconnected"; + setConnected(false); + stop(); +} + +void BluetoothServer::onControllerStateChanged(const QLowEnergyController::ControllerState &state) +{ + switch (state) { + case QLowEnergyController::UnconnectedState: + qCDebug(dcBluetoothServer()) << "Controller state disonnected."; + setConnected(false); + break; + case QLowEnergyController::ConnectingState: + qCDebug(dcBluetoothServer()) << "Controller state connecting..."; + setConnected(false); + m_advertisingTimer->stop(); + break; + case QLowEnergyController::ConnectedState: + qCDebug(dcBluetoothServer()) << "Controller state connected." << m_controller->remoteName() << m_controller->remoteAddress(); + setConnected(true); + m_advertisingTimer->stop(); + break; + case QLowEnergyController::DiscoveringState: + qCDebug(dcBluetoothServer()) << "Controller state discovering..."; + break; + case QLowEnergyController::DiscoveredState: + qCDebug(dcBluetoothServer()) << "Controller state discovered."; + break; + case QLowEnergyController::ClosingState: + qCDebug(dcBluetoothServer()) << "Controller state closing..."; + break; + case QLowEnergyController::AdvertisingState: + qCDebug(dcBluetoothServer()) << "Controller state advertising..."; + setRunning(true); + break; + default: + break; + } +} + +void BluetoothServer::onAdvertisingTimeout() +{ + qCDebug(dcBluetoothServer()) << "Advertising timeout."; + stop(); +} + +void BluetoothServer::characteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value) +{ + qCDebug(dcBluetoothServer()) << "Service characteristic changed" << characteristic.uuid() << value; +} + +void BluetoothServer::characteristicRead(const QLowEnergyCharacteristic &characteristic, const QByteArray &value) +{ + qCDebug(dcBluetoothServer()) << "Service characteristic read" << characteristic.uuid() << value; +} + +void BluetoothServer::characteristicWritten(const QLowEnergyCharacteristic &characteristic, const QByteArray &value) +{ + qCDebug(dcBluetoothServer()) << "Service characteristic written" << characteristic.uuid() << value; +} + +void BluetoothServer::descriptorRead(const QLowEnergyDescriptor &descriptor, const QByteArray &value) +{ + qCDebug(dcBluetoothServer()) << "Descriptor read" << descriptor.uuid() << value; +} + +void BluetoothServer::descriptorWritten(const QLowEnergyDescriptor &descriptor, const QByteArray &value) +{ + qCDebug(dcBluetoothServer()) << "Descriptor written" << descriptor.uuid() << value; +} + +void BluetoothServer::serviceError(const QLowEnergyService::ServiceError &error) +{ + QString errorString; + switch (error) { + case QLowEnergyService::NoError: + errorString = "No error"; + break; + case QLowEnergyService::OperationError: + errorString = "Operation error"; + break; + case QLowEnergyService::CharacteristicReadError: + errorString = "Characteristic read error"; + break; + case QLowEnergyService::CharacteristicWriteError: + errorString = "Characteristic write error"; + break; + case QLowEnergyService::DescriptorReadError: + errorString = "Descriptor read error"; + break; + case QLowEnergyService::DescriptorWriteError: + errorString = "Descriptor write error"; + break; + case QLowEnergyService::UnknownError: + errorString = "Unknown error"; + break; + default: + errorString = "Unhandled error"; + break; + } + + qCWarning(dcBluetoothServer()) << "Service error:" << errorString; +} + +void BluetoothServer::start() +{ + // Check if a user is connected + if (connected()) { + qCDebug(dcBluetoothServer()) << "User is connected. Doing nothing."; + return; + } + + if (running()) { + qCDebug(dcBluetoothServer()) << "Already running. Restart advertise timer." << Loopd::instance()->advertisingTimeout() << "[s]."; + m_localDevice->setHostMode(QBluetoothLocalDevice::HostDiscoverable); + m_advertisingTimer->stop(); + m_advertisingTimer->setSingleShot(true); + m_advertisingTimer->setInterval(Loopd::instance()->advertisingTimeout() * 1000); + m_advertisingTimer->start(); + return; + } + + qCDebug(dcBluetoothServer()) << "-------------------------------------"; + qCDebug(dcBluetoothServer()) << "Starting bluetooth server for" << Loopd::instance()->advertisingTimeout() << "[s]."; + m_advertisingTimer->stop(); + m_advertisingTimer->setSingleShot(true); + m_advertisingTimer->setInterval(Loopd::instance()->advertisingTimeout() * 1000); + qCDebug(dcBluetoothServer()) << "-------------------------------------"; + + // Local bluetooth device + m_localDevice = new QBluetoothLocalDevice(this); + if (!m_localDevice->isValid()) { + qCCritical(dcBluetoothServer()) << "Local bluetooth device is not valid."; + m_advertisingTimer->stop(); + delete m_localDevice; + m_localDevice = nullptr; + return; + } + + connect(m_localDevice, &QBluetoothLocalDevice::hostModeStateChanged, this, &BluetoothServer::onHostModeStateChanged); + connect(m_localDevice, &QBluetoothLocalDevice::deviceConnected, this, &BluetoothServer::onDeviceConnected); + connect(m_localDevice, &QBluetoothLocalDevice::deviceDisconnected, this, &BluetoothServer::onDeviceDisconnected); + + qCDebug(dcBluetoothServer()) << "Local device" << m_localDevice->name() << m_localDevice->address().toString(); + m_localDevice->setHostMode(QBluetoothLocalDevice::HostDiscoverable); + m_localDevice->powerOn(); + + + // Bluetooth low energy periperal controller + m_controller = QLowEnergyController::createPeripheral(this); + connect(m_controller, &QLowEnergyController::stateChanged, this, &BluetoothServer::onControllerStateChanged); + connect(m_controller, &QLowEnergyController::connected, this, &BluetoothServer::onConnected); + connect(m_controller, &QLowEnergyController::disconnected, this, &BluetoothServer::onDisconnected); + connect(m_controller, SIGNAL(error(QLowEnergyController::Error)), this, SLOT(onError(QLowEnergyController::Error))); + + // Note: https://www.bluetooth.com/specifications/gatt/services + m_deviceInfoService = m_controller->addService(deviceInformationServiceData(), m_controller); + m_genericAccessService = m_controller->addService(genericAccessServiceData(), m_controller); + m_genericAttributeService = m_controller->addService(genericAttributeServiceData(), m_controller); + + // Create services + m_networkService = new NetworkService(m_controller->addService(NetworkService::serviceData(), m_controller), m_controller); + m_wirelessService = new WirelessService(m_controller->addService(WirelessService::serviceData(), m_controller), m_controller); + m_systemService = new SystemService(m_controller->addService(SystemService::serviceData(), m_controller), m_controller); + + QLowEnergyAdvertisingData advertisingData; + advertisingData.setDiscoverability(QLowEnergyAdvertisingData::DiscoverabilityGeneral); + advertisingData.setIncludePowerLevel(true); + advertisingData.setLocalName("Loop-box"); + // TODO: set guh manufacturer SIG data + + + // Note: start advertising in 100 ms interval, this makes the device better discoverable on certain phones + QLowEnergyAdvertisingParameters advertisingParameters; + advertisingParameters.setInterval(100,100); + + qCDebug(dcBluetoothServer()) << "Start advertising loopd" << m_localDevice->address().toString(); + m_controller->startAdvertising(advertisingParameters, advertisingData, advertisingData); + + // Start the advertising timer + m_advertisingTimer->start(); + setRunning(true); +} + +void BluetoothServer::stop() +{ + if (connected() && m_controller) { + m_controller->disconnectFromDevice(); + return; + } + + qCDebug(dcBluetoothServer()) << "-------------------------------------"; + qCDebug(dcBluetoothServer()) << "Stopping bluetooth server."; + qCDebug(dcBluetoothServer()) << "-------------------------------------"; + + if (m_controller) { + qCDebug(dcBluetoothServer()) << "Stop advertising."; + m_controller->stopAdvertising(); + delete m_controller; + m_controller = nullptr; + } + + if (m_localDevice) { + qCDebug(dcBluetoothServer()) << "Set host mode to connectable."; + m_localDevice->setHostMode(QBluetoothLocalDevice::HostConnectable); + delete m_localDevice; + m_localDevice = nullptr; + } + + setConnected(false); + setRunning(false); +} + diff --git a/nymea-networkmanager/bluetooth/bluetoothserver.h b/nymea-networkmanager/bluetooth/bluetoothserver.h new file mode 100644 index 0000000..d93d0d7 --- /dev/null +++ b/nymea-networkmanager/bluetooth/bluetoothserver.h @@ -0,0 +1,103 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of loopd. * + * * + * Loopd can not be copied and/or distributed without the express * + * permission of guh GmbH. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef BLUETOOTHSERVER_H +#define BLUETOOTHSERVER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "networkservice.h" +#include "wirelessservice.h" +#include "systemservice.h" +#include "networkmanager/networkmanager.h" + +class BluetoothServer : public QObject +{ + Q_OBJECT +public: + + explicit BluetoothServer(const QString &machineId, QObject *parent = 0); + ~BluetoothServer(); + + bool running() const; + bool connected() const; + +private: + QString m_machineId; + QBluetoothLocalDevice *m_localDevice = nullptr; + QLowEnergyController *m_controller = nullptr; + + QLowEnergyService *m_deviceInfoService = nullptr; + QLowEnergyService *m_genericAccessService = nullptr; + QLowEnergyService *m_genericAttributeService = nullptr; + + NetworkService *m_networkService = nullptr; + WirelessService *m_wirelessService = nullptr; + SystemService *m_systemService = nullptr; + + QTimer *m_advertisingTimer = nullptr; + + bool m_running = false; + bool m_connected = false; + + QLowEnergyServiceData deviceInformationServiceData(); + QLowEnergyServiceData genericAccessServiceData(); + QLowEnergyServiceData genericAttributeServiceData(); + + void setRunning(const bool &running); + void setConnected(const bool &connected); + +signals: + void runningChanged(const bool &running); + void connectedChanged(const bool &connected); + +private slots: + // Local bluetooth device + void onHostModeStateChanged(const QBluetoothLocalDevice::HostMode mode); + void onDeviceConnected(const QBluetoothAddress &address); + void onDeviceDisconnected(const QBluetoothAddress &address); + void onError(const QLowEnergyController::Error &error); + + // Bluetooth controller + void onConnected(); + void onDisconnected(); + void onControllerStateChanged(const QLowEnergyController::ControllerState &state); + + void onAdvertisingTimeout(); + + // Services + void characteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value); + void characteristicRead(const QLowEnergyCharacteristic &characteristic, const QByteArray &value); + void characteristicWritten(const QLowEnergyCharacteristic &characteristic, const QByteArray &value); + void descriptorRead(const QLowEnergyDescriptor &descriptor, const QByteArray &value); + void descriptorWritten(const QLowEnergyDescriptor &descriptor, const QByteArray &value); + void serviceError(const QLowEnergyService::ServiceError &error); + +public slots: + void start(); + void stop(); + +}; + +#endif // BLUETOOTHSERVER_H diff --git a/nymea-networkmanager/bluetooth/bluetoothuuids.h b/nymea-networkmanager/bluetooth/bluetoothuuids.h new file mode 100644 index 0000000..bdf420e --- /dev/null +++ b/nymea-networkmanager/bluetooth/bluetoothuuids.h @@ -0,0 +1,35 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of loopd. * + * * + * Loopd can not be copied and/or distributed without the express * + * permission of guh GmbH. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef BLUETOOTHUUIDS_H +#define BLUETOOTHUUIDS_H + +#include + +static QBluetoothUuid networkServiceUuid = QBluetoothUuid(QUuid("ef6d6610-b8af-49e0-9eca-ab343513641c")); +static QBluetoothUuid networkStatusCharacteristicUuid = QBluetoothUuid(QUuid("ef6d6611-b8af-49e0-9eca-ab343513641c")); +static QBluetoothUuid networkCommanderCharacteristicUuid = QBluetoothUuid(QUuid("ef6d6612-b8af-49e0-9eca-ab343513641c")); +static QBluetoothUuid networkResponseCharacteristicUuid = QBluetoothUuid(QUuid("ef6d6613-b8af-49e0-9eca-ab343513641c")); +static QBluetoothUuid networkingEnabledCharacteristicUuid = QBluetoothUuid(QUuid("ef6d6614-b8af-49e0-9eca-ab343513641c")); +static QBluetoothUuid wirelessEnabledCharacteristicUuid = QBluetoothUuid(QUuid("ef6d6615-b8af-49e0-9eca-ab343513641c")); + +static QBluetoothUuid wirelessServiceUuid = QBluetoothUuid(QUuid("e081fec0-f757-4449-b9c9-bfa83133f7fc")); +static QBluetoothUuid wirelessCommanderCharacteristicUuid = QBluetoothUuid(QUuid("e081fec1-f757-4449-b9c9-bfa83133f7fc")); +static QBluetoothUuid wirelessResponseCharacteristicUuid = QBluetoothUuid(QUuid("e081fec2-f757-4449-b9c9-bfa83133f7fc")); +static QBluetoothUuid wirelessStateCharacteristicUuid = QBluetoothUuid(QUuid("e081fec3-f757-4449-b9c9-bfa83133f7fc")); + +static QBluetoothUuid systemServiceUuid = QBluetoothUuid(QUuid("e081fed0-f757-4449-b9c9-bfa83133f7fc")); +static QBluetoothUuid systemCommanderCharacteristicUuid = QBluetoothUuid(QUuid("e081fed1-f757-4449-b9c9-bfa83133f7fc")); +static QBluetoothUuid systemResponseCharacteristicUuid = QBluetoothUuid(QUuid("e081fed2-f757-4449-b9c9-bfa83133f7fc")); +static QBluetoothUuid systemUpdateCharacteristicUuid = QBluetoothUuid(QUuid("e081fed3-f757-4449-b9c9-bfa83133f7fc")); + + +#endif // BLUETOOTHUUIDS_H diff --git a/nymea-networkmanager/bluetooth/networkservice.cpp b/nymea-networkmanager/bluetooth/networkservice.cpp new file mode 100644 index 0000000..be0b1b9 --- /dev/null +++ b/nymea-networkmanager/bluetooth/networkservice.cpp @@ -0,0 +1,340 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of loopd. * + * * + * Loopd can not be copied and/or distributed without the express * + * permission of guh GmbH. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "networkservice.h" +#include "bluetoothuuids.h" +#include "loggingcategory.h" +#include "loopd.h" + +#include +#include + +NetworkService::NetworkService(QLowEnergyService *service, QObject *parent) : + QObject(parent), + m_service(service) +{ + qCDebug(dcBluetoothServer()) << "Create NetworkService."; + + // Service + connect(m_service, SIGNAL(characteristicChanged(QLowEnergyCharacteristic, QByteArray)), this, SLOT(characteristicChanged(QLowEnergyCharacteristic, QByteArray))); + connect(m_service, SIGNAL(characteristicRead(QLowEnergyCharacteristic, QByteArray)), this, SLOT(characteristicChanged(QLowEnergyCharacteristic, QByteArray))); + connect(m_service, SIGNAL(characteristicWritten(QLowEnergyCharacteristic, QByteArray)), this, SLOT(characteristicWritten(QLowEnergyCharacteristic, QByteArray))); + connect(m_service, SIGNAL(descriptorWritten(QLowEnergyDescriptor, QByteArray)), this, SLOT(descriptorWritten(QLowEnergyDescriptor, QByteArray))); + connect(m_service, SIGNAL(error(QLowEnergyService::ServiceError)), this, SLOT(serviceError(QLowEnergyService::ServiceError))); + + // NetworkManager + connect(Loopd::instance()->networkManager(), &NetworkManager::stateChanged, this, &NetworkService::onNetworkManagerStateChanged); + connect(Loopd::instance()->networkManager(), &NetworkManager::networkingEnabledChanged, this, &NetworkService::onNetworkingEnabledChanged); + connect(Loopd::instance()->networkManager(), &NetworkManager::wirelessEnabledChanged, this, &NetworkService::onWirelessEnabledChanged); +} + +QLowEnergyService *NetworkService::service() +{ + return m_service; +} + +QLowEnergyServiceData NetworkService::serviceData() +{ + QLowEnergyServiceData serviceData; + serviceData.setType(QLowEnergyServiceData::ServiceTypePrimary); + serviceData.setUuid(networkServiceUuid); + + QLowEnergyDescriptorData clientConfigDescriptorData(QBluetoothUuid::ClientCharacteristicConfiguration, QByteArray(2, 0)); + + // Network manager status ef6d661-b8af-49e0-9eca-ab343513641c + QLowEnergyCharacteristicData networkStatusData; + networkStatusData.setUuid(networkStatusCharacteristicUuid); + networkStatusData.setValue(QByteArray(1, 0)); + networkStatusData.setProperties(QLowEnergyCharacteristic::Read | QLowEnergyCharacteristic::Notify); + networkStatusData.addDescriptor(clientConfigDescriptorData); + networkStatusData.setValue(NetworkService::getNetworkManagerStateByteArray(Loopd::instance()->networkManager()->state())); + serviceData.addCharacteristic(networkStatusData); + + // Network manager commander ef6d6612-b8af-49e0-9eca-ab343513641c + QLowEnergyCharacteristicData networkCommanderCharacteristicData; + networkCommanderCharacteristicData.setUuid(networkCommanderCharacteristicUuid); + networkCommanderCharacteristicData.setProperties(QLowEnergyCharacteristic::Write); + networkCommanderCharacteristicData.setValueLength(1, 1); + serviceData.addCharacteristic(networkCommanderCharacteristicData); + + // Response characteristic ef6d6613-b8af-49e0-9eca-ab343513641c + QLowEnergyCharacteristicData networkResponseCharacteristicData; + networkResponseCharacteristicData.setUuid(networkResponseCharacteristicUuid); + networkResponseCharacteristicData.setProperties(QLowEnergyCharacteristic::Notify); + networkResponseCharacteristicData.addDescriptor(clientConfigDescriptorData); + networkResponseCharacteristicData.setValueLength(1, 1); + serviceData.addCharacteristic(networkResponseCharacteristicData); + + // Networking enabled ef6d6614-b8af-49e0-9eca-ab343513641c + QLowEnergyCharacteristicData networkingEnabledStatusData; + networkingEnabledStatusData.setUuid(networkingEnabledCharacteristicUuid); + networkingEnabledStatusData.setValue(QByteArray(1, 0)); + networkingEnabledStatusData.setProperties(QLowEnergyCharacteristic::Read | QLowEnergyCharacteristic::Notify); + networkingEnabledStatusData.setValue(QByteArray::number((int)Loopd::instance()->networkManager()->networkingEnabled())); + serviceData.addCharacteristic(networkingEnabledStatusData); + + // Wireless enabled ef6d6615-b8af-49e0-9eca-ab343513641c + QLowEnergyCharacteristicData wirelessEnabledStatusData; + wirelessEnabledStatusData.setUuid(wirelessEnabledCharacteristicUuid); + wirelessEnabledStatusData.setValue(QByteArray(1, 0)); + wirelessEnabledStatusData.setProperties(QLowEnergyCharacteristic::Read | QLowEnergyCharacteristic::Notify); + wirelessEnabledStatusData.setValue(QByteArray::number((int)Loopd::instance()->networkManager()->wirelessEnabled())); + serviceData.addCharacteristic(wirelessEnabledStatusData); + + return serviceData; +} + +QByteArray NetworkService::getNetworkManagerStateByteArray(const NetworkManager::NetworkManagerState &state) +{ + QByteArray networkManagerState; + switch (state) { + case NetworkManager::NetworkManagerStateUnknown: + networkManagerState = QByteArray::fromHex("00"); + break; + case NetworkManager::NetworkManagerStateAsleep: + networkManagerState = QByteArray::fromHex("01"); + break; + case NetworkManager::NetworkManagerStateDisconnected: + networkManagerState = QByteArray::fromHex("02"); + break; + case NetworkManager::NetworkManagerStateDisconnecting: + networkManagerState = QByteArray::fromHex("03"); + break; + case NetworkManager::NetworkManagerStateConnecting: + networkManagerState = QByteArray::fromHex("04"); + break; + case NetworkManager::NetworkManagerStateConnectedLocal: + networkManagerState = QByteArray::fromHex("05"); + break; + case NetworkManager::NetworkManagerStateConnectedSite: + networkManagerState = QByteArray::fromHex("06"); + break; + case NetworkManager::NetworkManagerStateConnectedGlobal: + networkManagerState = QByteArray::fromHex("07"); + break; + } + + return networkManagerState; +} + +void NetworkService::sendResponse(const NetworkService::NetworkServiceResponse &response) +{ + if (!m_service) { + qCWarning(dcBluetoothServer()) << "NetworkService: Could not send response. Service not valid"; + return; + } + + QLowEnergyCharacteristic characteristic = m_service->characteristic(networkResponseCharacteristicUuid); + if (!characteristic.isValid()) { + qCWarning(dcBluetoothServer()) << "NetworkService: Could not send response. Characteristic not valid"; + return; + } + + switch (response) { + case NetworkServiceResponseSuccess: + m_service->writeCharacteristic(characteristic, QByteArray::fromHex("00")); + break; + case NetworkServiceResponseIvalidValue: + m_service->writeCharacteristic(characteristic, QByteArray::fromHex("01")); + break; + case NetworkServiceResponseNetworkManagerNotAvailable: + m_service->writeCharacteristic(characteristic, QByteArray::fromHex("02")); + break; + case NetworkServiceResponseWirelessNotAvailable: + m_service->writeCharacteristic(characteristic, QByteArray::fromHex("03")); + break; + default: + // Unknown error + m_service->writeCharacteristic(characteristic, QByteArray::fromHex("04")); + break; + } +} + +NetworkService::NetworkServiceCommand NetworkService::verifyCommand(const QByteArray &commandData) +{ + if (commandData.length() != 1) + return NetworkServiceCommandInvalid; + + uint commandInteger = commandData.toHex().toUInt(0, 16); + switch (commandInteger) { + case NetworkServiceCommandEnableNetworking: + return NetworkServiceCommandEnableNetworking; + case NetworkServiceCommandDisableNetworking: + return NetworkServiceCommandDisableNetworking; + case NetworkServiceCommandEnableWireless: + return NetworkServiceCommandEnableWireless; + case NetworkServiceCommandDisableWireless: + return NetworkServiceCommandDisableWireless; + default: + break; + } + + return NetworkServiceCommandInvalid; +} + +void NetworkService::characteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value) +{ + if (characteristic.uuid() == networkCommanderCharacteristicUuid) { + + NetworkServiceCommand command = verifyCommand(value); + if (command == NetworkServiceCommandInvalid) { + qCWarning(dcBluetoothServer()) << "NetworkService: received invalid command" << command; + sendResponse(NetworkServiceResponseIvalidValue); + return; + } + + if (!Loopd::instance()->networkManager()->available()) { + qCWarning(dcBluetoothServer()) << "NetworkService: Networkmanager not available"; + sendResponse(NetworkServiceResponseNetworkManagerNotAvailable); + return; + } + + processCommand(command); + + sendResponse(NetworkServiceResponseSuccess); + return; + } + + qCDebug(dcBluetoothServer()) << "NetworkService: Characteristic changed" << characteristic.uuid().toString() << value; +} + +void NetworkService::characteristicRead(const QLowEnergyCharacteristic &characteristic, const QByteArray &value) +{ + qCDebug(dcBluetoothServer()) << "NetworkService: Characteristic read" << characteristic.uuid().toString() << value; +} + +void NetworkService::characteristicWritten(const QLowEnergyCharacteristic &characteristic, const QByteArray &value) +{ + qCDebug(dcBluetoothServer()) << "NetworkService: Characteristic written" << characteristic.uuid().toString() << value; +} + +void NetworkService::descriptorRead(const QLowEnergyDescriptor &descriptor, const QByteArray &value) +{ + qCDebug(dcBluetoothServer()) << "NetworkService: Descriptor read" << descriptor.uuid().toString() << value; +} + +void NetworkService::descriptorWritten(const QLowEnergyDescriptor &descriptor, const QByteArray &value) +{ + qCDebug(dcBluetoothServer()) << "NetworkService: Descriptor written" << descriptor.uuid().toString() << value; +} + +void NetworkService::serviceError(const QLowEnergyService::ServiceError &error) +{ + QString errorString; + switch (error) { + case QLowEnergyService::NoError: + errorString = "No error"; + break; + case QLowEnergyService::OperationError: + errorString = "Operation error"; + break; + case QLowEnergyService::CharacteristicReadError: + errorString = "Characteristic read error"; + break; + case QLowEnergyService::CharacteristicWriteError: + errorString = "Characteristic write error"; + break; + case QLowEnergyService::DescriptorReadError: + errorString = "Descriptor read error"; + break; + case QLowEnergyService::DescriptorWriteError: + errorString = "Descriptor write error"; + break; + case QLowEnergyService::UnknownError: + errorString = "Unknown error"; + break; + default: + errorString = "Unhandled error"; + break; + } + + qCWarning(dcBluetoothServer()) << "NetworkService: Error:" << errorString; +} + +void NetworkService::processCommand(const NetworkServiceCommand &command) +{ + switch (command) { + case NetworkServiceCommandEnableNetworking: + qCDebug(dcBluetoothServer()) << "NetworkService: received \"Enable networking\" command"; + Loopd::instance()->networkManager()->enableNetworking(true); + break; + case NetworkServiceCommandDisableNetworking: + qCDebug(dcBluetoothServer()) << "NetworkService: received \"Disable networking\" command"; + Loopd::instance()->networkManager()->enableNetworking(false); + break; + case NetworkServiceCommandEnableWireless: + qCDebug(dcBluetoothServer()) << "NetworkService: received \"Enable wireless networking\" command"; + Loopd::instance()->networkManager()->enableWireless(true); + break; + case NetworkServiceCommandDisableWireless: + qCDebug(dcBluetoothServer()) << "NetworkService: received \"Disable wireless networking\" command"; + Loopd::instance()->networkManager()->enableWireless(false); + break; + default: + qCWarning(dcBluetoothServer()) << "NetworkService: Unhandled command" << command; + sendResponse(NetworkServiceResponseIvalidValue); + break; + } +} + +bool NetworkService::onNetworkManagerStateChanged() +{ + if (!m_service) { + qCWarning(dcBluetoothServer()) << "NetworkService: Could not updatet network manager status. Service not valid"; + return false; + } + + QLowEnergyCharacteristic characteristic = m_service->characteristic(networkStatusCharacteristicUuid); + if (!characteristic.isValid()) { + qCWarning(dcBluetoothServer()) << "NetworkService: Could not update network manager status. Characteristic not valid"; + return false; + } + + qCDebug(dcBluetoothServer()) << "NetworkService: Notify state changed" << NetworkService::getNetworkManagerStateByteArray(Loopd::instance()->networkManager()->state()); + m_service->writeCharacteristic(characteristic, NetworkService::getNetworkManagerStateByteArray(Loopd::instance()->networkManager()->state())); + return true; +} + +bool NetworkService::onNetworkingEnabledChanged() +{ + if (!m_service) { + qCWarning(dcBluetoothServer()) << "NetworkService: Could not set networking enabled. Service not valid"; + return false; + } + + QLowEnergyCharacteristic characteristic = m_service->characteristic(networkingEnabledCharacteristicUuid); + if (!characteristic.isValid()) { + qCWarning(dcBluetoothServer()) << "NetworkService: Could not set networking enabled. Characteristic not valid"; + return false; + } + + qCDebug(dcBluetoothServer()) << "NetworkService: Notify networking enabled changed:" << (Loopd::instance()->networkManager()->networkingEnabled() ? "enabled" : "disabled"); + m_service->writeCharacteristic(characteristic, Loopd::instance()->networkManager()->networkingEnabled() ? QByteArray::fromHex("01") : QByteArray::fromHex("00")); + return true; +} + +bool NetworkService::onWirelessEnabledChanged() +{ + if (!m_service) { + qCWarning(dcBluetoothServer()) << "NetworkService: Could not set wireless enabled. Service not valid"; + return false; + } + + QLowEnergyCharacteristic characteristic = m_service->characteristic(wirelessEnabledCharacteristicUuid); + if (!characteristic.isValid()) { + qCWarning(dcBluetoothServer()) << "NetworkService: Could not set wireless enabled. Characteristic not valid"; + return false; + } + + qCDebug(dcBluetoothServer()) << "NetworkService: Notify wireless networking enabled changed:" << (Loopd::instance()->networkManager()->wirelessEnabled() ? "enabled" : "disabled"); + m_service->writeCharacteristic(characteristic, Loopd::instance()->networkManager()->wirelessEnabled() ? QByteArray::fromHex("01") : QByteArray::fromHex("00")); + return true; +} diff --git a/nymea-networkmanager/bluetooth/networkservice.h b/nymea-networkmanager/bluetooth/networkservice.h new file mode 100644 index 0000000..f5cfda1 --- /dev/null +++ b/nymea-networkmanager/bluetooth/networkservice.h @@ -0,0 +1,78 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of loopd. * + * * + * Loopd can not be copied and/or distributed without the express * + * permission of guh GmbH. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef NETWORKSERVICE_H +#define NETWORKSERVICE_H + +#include +#include +#include + +#include "networkmanager/networkmanager.h" + +class NetworkService : public QObject +{ + Q_OBJECT + Q_ENUMS(NetworkServiceCommand) + Q_ENUMS(NetworkServiceResponse) + +public: + enum NetworkServiceCommand { + NetworkServiceCommandInvalid = -1, + NetworkServiceCommandEnableNetworking = 0x00, + NetworkServiceCommandDisableNetworking = 0x01, + NetworkServiceCommandEnableWireless = 0x02, + NetworkServiceCommandDisableWireless = 0x03 + }; + Q_ENUM(NetworkServiceCommand) + + enum NetworkServiceResponse { + NetworkServiceResponseSuccess = 0x00, + NetworkServiceResponseIvalidValue = 0x01, + NetworkServiceResponseNetworkManagerNotAvailable = 0x02, + NetworkServiceResponseWirelessNotAvailable = 0x03, + NetworkServiceResponseUnknownError = 0x04, + }; + Q_ENUM(NetworkServiceResponse) + + explicit NetworkService(QLowEnergyService *service, QObject *parent = 0); + + QLowEnergyService *service(); + + static QLowEnergyServiceData serviceData(); + static QByteArray getNetworkManagerStateByteArray(const NetworkManager::NetworkManagerState &state); + +private: + QLowEnergyService *m_service; + void sendResponse(const NetworkServiceResponse &response); + + NetworkServiceCommand verifyCommand(const QByteArray &commandData); + +private slots: + // Service + void characteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value); + void characteristicRead(const QLowEnergyCharacteristic &characteristic, const QByteArray &value); + void characteristicWritten(const QLowEnergyCharacteristic &characteristic, const QByteArray &value); + void descriptorRead(const QLowEnergyDescriptor &descriptor, const QByteArray &value); + void descriptorWritten(const QLowEnergyDescriptor &descriptor, const QByteArray &value); + void serviceError(const QLowEnergyService::ServiceError &error); + + // Commands + void processCommand(const NetworkServiceCommand &command); + +public slots: + bool onNetworkManagerStateChanged(); + bool onNetworkingEnabledChanged(); + bool onWirelessEnabledChanged(); + +}; + +#endif // NETWORKSERVICE_H diff --git a/nymea-networkmanager/bluetooth/systemservice.cpp b/nymea-networkmanager/bluetooth/systemservice.cpp new file mode 100644 index 0000000..c251f79 --- /dev/null +++ b/nymea-networkmanager/bluetooth/systemservice.cpp @@ -0,0 +1,256 @@ +#include "systemservice.h" +#include "bluetoothuuids.h" +#include "loggingcategory.h" +#include "loopd.h" + +#include +#include +#include +#include + +SystemService::SystemService(QLowEnergyService *service, QObject *parent) : + QObject(parent), + m_service(service) +{ + connect(m_service, &QLowEnergyService::characteristicChanged, this, &SystemService::characteristicChanged); + connect(m_service, &QLowEnergyService::characteristicRead, this, &SystemService::characteristicChanged); + connect(m_service, &QLowEnergyService::characteristicWritten, this, &SystemService::characteristicWritten); + connect(m_service, &QLowEnergyService::descriptorWritten, this, &SystemService::descriptorWritten); + connect(m_service, SIGNAL(error(QLowEnergyService::ServiceError)), this, SLOT(serviceError(QLowEnergyService::ServiceError))); + + connect(Loopd::instance()->snapdControl(), &SnapdControl::updateRunningChanged, this, &SystemService::onUpdateRunningChanged); +} + +QLowEnergyService *SystemService::service() +{ + return m_service; +} + +QLowEnergyServiceData SystemService::serviceData() +{ + QLowEnergyServiceData serviceData; + serviceData.setType(QLowEnergyServiceData::ServiceTypePrimary); + serviceData.setUuid(systemServiceUuid); + + QLowEnergyDescriptorData clientConfigDescriptorData(QBluetoothUuid::ClientCharacteristicConfiguration, QByteArray(2, 0)); + + // System commander characterisitc e081fed1-f757-4449-b9c9-bfa83133f7fc + QLowEnergyCharacteristicData systemCommanderCharacteristicData; + systemCommanderCharacteristicData.setUuid(systemCommanderCharacteristicUuid); + systemCommanderCharacteristicData.setProperties(QLowEnergyCharacteristic::Write); + systemCommanderCharacteristicData.setValueLength(0, 20); + serviceData.addCharacteristic(systemCommanderCharacteristicData); + + // Response characterisitc e081fed2-f757-4449-b9c9-bfa83133f7fc + QLowEnergyCharacteristicData systemResponseCharacteristicData; + systemResponseCharacteristicData.setUuid(systemResponseCharacteristicUuid); + systemResponseCharacteristicData.setProperties(QLowEnergyCharacteristic::Notify); + systemResponseCharacteristicData.addDescriptor(clientConfigDescriptorData); + systemResponseCharacteristicData.setValueLength(0, 20); + serviceData.addCharacteristic(systemResponseCharacteristicData); + return serviceData; + + // System update indicator characterisitc e081fed3-f757-4449-b9c9-bfa83133f7fc + QLowEnergyCharacteristicData systemUpdateCharacteristicData; + systemUpdateCharacteristicData.setUuid(systemUpdateCharacteristicUuid); + systemUpdateCharacteristicData.setProperties(QLowEnergyCharacteristic::Read | QLowEnergyCharacteristic::Notify); + systemUpdateCharacteristicData.addDescriptor(clientConfigDescriptorData); + systemUpdateCharacteristicData.setValueLength(1, 1); + systemUpdateCharacteristicData.setValue(QByteArray::number((int)Loopd::instance()->snapdControl()->updateRunning())); + serviceData.addCharacteristic(systemUpdateCharacteristicData); + return serviceData; +} + +void SystemService::streamData(const QVariantMap &responseMap) +{ + QLowEnergyCharacteristic characteristic = m_service->characteristic(systemResponseCharacteristicUuid); + if (!characteristic.isValid()) { + qCWarning(dcBluetoothServer()) << "SystemService: System response characteristic not valid"; + return; + } + + QByteArray data = QJsonDocument::fromVariant(responseMap).toJson(QJsonDocument::Compact) + '\n'; + qCDebug(dcBluetoothServer()) << "SystemService: Start streaming response data:" << data.count() << "bytes"; + + int sentDataLength = 0; + QByteArray remainingData = data; + while (!remainingData.isEmpty()) { + QByteArray package = remainingData.left(20); + sentDataLength += package.count(); + m_service->writeCharacteristic(characteristic, package); + remainingData = remainingData.remove(0, package.count()); + } + + qCDebug(dcBluetoothServer()) << "SystemService: Finished streaming response data"; +} + +QVariantMap SystemService::createResponse(const SystemService::SystemServiceCommand &command, const SystemService::SystemServiceResponse &responseCode) +{ + QVariantMap response; + response.insert("c", (int)command); + response.insert("r", (int)responseCode); + return response; +} + +void SystemService::characteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value) +{ + // Command + if (characteristic.uuid() == systemCommanderCharacteristicUuid) { + // Check if currently reading + if (m_readingInputData) { + m_inputDataStream.append(value); + } else { + m_inputDataStream.clear(); + m_readingInputData = true; + m_inputDataStream.append(value); + } + + // If command finished + if (value.endsWith('\n')) { + QJsonParseError error; + QJsonDocument jsonDocument = QJsonDocument::fromJson(m_inputDataStream, &error); + if (error.error != QJsonParseError::NoError) { + qCWarning(dcBluetoothServer()) << "SystemService: Got invalid json object" << m_inputDataStream; + m_inputDataStream.clear(); + m_readingInputData = false; + return; + } + + qCDebug(dcBluetoothServer()) << "SystemService: Got command stream" << jsonDocument.toJson(); + + processCommand(jsonDocument.toVariant().toMap()); + + m_inputDataStream.clear(); + m_readingInputData = false; + } + + // Limit possible data stream to prevent overflow + if (m_inputDataStream.length() >= 20 * 1024) { + m_inputDataStream.clear(); + m_readingInputData = false; + return; + } + } +} + +void SystemService::characteristicRead(const QLowEnergyCharacteristic &characteristic, const QByteArray &value) +{ + qCDebug(dcBluetoothServer()) << "SystemService: Characteristic read" << characteristic.uuid().toString() << value; +} + +void SystemService::characteristicWritten(const QLowEnergyCharacteristic &characteristic, const QByteArray &value) +{ + qCDebug(dcBluetoothServer()) << "SystemService: Characteristic written" << characteristic.uuid().toString() << value; +} + +void SystemService::descriptorRead(const QLowEnergyDescriptor &descriptor, const QByteArray &value) +{ + qCDebug(dcBluetoothServer()) << "SystemService: Descriptor read" << descriptor.uuid().toString() << value; +} + +void SystemService::descriptorWritten(const QLowEnergyDescriptor &descriptor, const QByteArray &value) +{ + qCDebug(dcBluetoothServer()) << "SystemService: Descriptor written" << descriptor.uuid().toString() << value; +} + +void SystemService::serviceError(const QLowEnergyService::ServiceError &error) +{ + QString errorString; + switch (error) { + case QLowEnergyService::NoError: + errorString = "No error"; + break; + case QLowEnergyService::OperationError: + errorString = "Operation error"; + break; + case QLowEnergyService::CharacteristicReadError: + errorString = "Characteristic read error"; + break; + case QLowEnergyService::CharacteristicWriteError: + errorString = "Characteristic write error"; + break; + case QLowEnergyService::DescriptorReadError: + errorString = "Descriptor read error"; + break; + case QLowEnergyService::DescriptorWriteError: + errorString = "Descriptor write error"; + break; + case QLowEnergyService::UnknownError: + errorString = "Unknown error"; + break; + default: + errorString = "Unhandled error"; + break; + } + + qCWarning(dcBluetoothServer()) << "SystemService: Error:" << errorString; +} + +void SystemService::processCommand(const QVariantMap &request) +{ + if (!request.contains("c")) { + qCWarning(dcBluetoothServer()) << "SystemService: Invalid request. Command value missing."; + streamData(createResponse(SystemServiceCommandPushAuthentication, SystemServiceResponseInvalidValue)); + return; + } + + bool commandIntOk; + int command = request.value("c").toInt(&commandIntOk); + if (!commandIntOk) { + qCWarning(dcBluetoothServer()) << "SystemService: Invalid request. Could not convert method to interger."; + streamData(createResponse(SystemServiceCommandPushAuthentication, SystemServiceResponseInvalidValue)); + return; + } + + // Process method + switch (command) { + case SystemServiceCommandPushAuthentication: + commandPressPushButton(); + break; + default: + qCWarning(dcBluetoothServer()) << "SystemService: Invalid request. Unknown command" << command; + streamData(createResponse(SystemServiceCommandPushAuthentication, SystemServiceResponseInvalidCommand)); + break; + } +} + +void SystemService::commandPressPushButton() +{ + if (!m_service) { + qCWarning(dcBluetoothServer()) << "SystemService: Could not press push button. Service not valid."; + return; + } + + QLowEnergyCharacteristic characteristic = m_service->characteristic(systemResponseCharacteristicUuid); + if (!characteristic.isValid()) { + qCWarning(dcBluetoothServer()) << "SystemService: System response characteristic not valid"; + return; + } + + if (!Loopd::instance()->nymeaService()->available()) { + streamData(createResponse(SystemServiceCommandPushAuthentication, SystemServiceResponsePushServiceUnavailable)); + return; + } + + Loopd::instance()->nymeaService()->pushButtonPressed(); + + streamData(createResponse(SystemServiceCommandPushAuthentication, SystemServiceResponseSuccess)); +} + +void SystemService::onUpdateRunningChanged(const bool &running) +{ + if (!m_service) { + qCWarning(dcBluetoothServer()) << "SystemService: Could not set system update running characteristic. Service not valid."; + return; + } + + QLowEnergyCharacteristic characteristic = m_service->characteristic(systemUpdateCharacteristicUuid); + if (!characteristic.isValid()) { + qCWarning(dcBluetoothServer()) << "SystemService: Could not set system update running characteristic. Characteristic not valid"; + return; + } + + qCDebug(dcBluetoothServer()) << "SystemService: Notify system update running changed:" << (running ? "running" : "finished"); + m_service->writeCharacteristic(characteristic, running ? QByteArray::fromHex("01") : QByteArray::fromHex("00")); + return; +} diff --git a/nymea-networkmanager/bluetooth/systemservice.h b/nymea-networkmanager/bluetooth/systemservice.h new file mode 100644 index 0000000..5504187 --- /dev/null +++ b/nymea-networkmanager/bluetooth/systemservice.h @@ -0,0 +1,61 @@ +#ifndef SYSTEMSERVICE_H +#define SYSTEMSERVICE_H + +#include +#include +#include + +class SystemService : public QObject +{ + Q_OBJECT +public: + enum SystemServiceCommand { + SystemServiceCommandInvalid = -1, + SystemServiceCommandPushAuthentication = 0x00 + }; + Q_ENUM(SystemServiceCommand) + + enum SystemServiceResponse { + SystemServiceResponseSuccess = 0x00, + SystemServiceResponseUnknownError = 0x01, + SystemServiceResponseInvalidCommand = 0x02, + SystemServiceResponseInvalidValue = 0x03, + SystemServiceResponsePushServiceUnavailable = 0x04, + }; + Q_ENUM(SystemServiceResponse) + + explicit SystemService(QLowEnergyService *service, QObject *parent = nullptr); + + QLowEnergyService *service(); + + static QLowEnergyServiceData serviceData(); + +private: + QLowEnergyService *m_service = nullptr; + + bool m_readingInputData = false; + QByteArray m_inputDataStream; + + void streamData(const QVariantMap &responseMap); + QVariantMap createResponse(const SystemServiceCommand &command, const SystemServiceResponse &responseCode = SystemServiceResponseSuccess); + + +private slots: + // Service + void characteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value); + void characteristicRead(const QLowEnergyCharacteristic &characteristic, const QByteArray &value); + void characteristicWritten(const QLowEnergyCharacteristic &characteristic, const QByteArray &value); + void descriptorRead(const QLowEnergyDescriptor &descriptor, const QByteArray &value); + void descriptorWritten(const QLowEnergyDescriptor &descriptor, const QByteArray &value); + void serviceError(const QLowEnergyService::ServiceError &error); + + // Commands + void processCommand(const QVariantMap &request); + + // Push button authentication + void commandPressPushButton(); + + void onUpdateRunningChanged(const bool &running); +}; + +#endif // SYSTEMSERVICE_H diff --git a/nymea-networkmanager/bluetooth/wirelessservice.cpp b/nymea-networkmanager/bluetooth/wirelessservice.cpp new file mode 100644 index 0000000..e5bf98f --- /dev/null +++ b/nymea-networkmanager/bluetooth/wirelessservice.cpp @@ -0,0 +1,500 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of loopd. * + * * + * Loopd can not be copied and/or distributed without the express * + * permission of guh GmbH. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "wirelessservice.h" +#include "bluetoothuuids.h" +#include "loggingcategory.h" +#include "loopd.h" + +#include +#include +#include +#include + +WirelessService::WirelessService(QLowEnergyService *service, QObject *parent) : + QObject(parent), + m_service(service), + m_device(nullptr), + m_readingInputData(false) +{ + qCDebug(dcBluetoothServer()) << "Create WirelessService."; + + // Service + connect(m_service, SIGNAL(characteristicChanged(QLowEnergyCharacteristic, QByteArray)), this, SLOT(characteristicChanged(QLowEnergyCharacteristic, QByteArray))); + connect(m_service, SIGNAL(characteristicRead(QLowEnergyCharacteristic, QByteArray)), this, SLOT(characteristicChanged(QLowEnergyCharacteristic, QByteArray))); + connect(m_service, SIGNAL(characteristicWritten(QLowEnergyCharacteristic, QByteArray)), this, SLOT(characteristicWritten(QLowEnergyCharacteristic, QByteArray))); + connect(m_service, SIGNAL(descriptorWritten(QLowEnergyDescriptor, QByteArray)), this, SLOT(descriptorWritten(QLowEnergyDescriptor, QByteArray))); + connect(m_service, SIGNAL(error(QLowEnergyService::ServiceError)), this, SLOT(serviceError(QLowEnergyService::ServiceError))); + + // Get the wireless network device if there is any + if (!Loopd::instance()->networkManager()->wirelessAvailable()) { + qCWarning(dcBluetoothServer()) << "WirelessService: There is no wireless network device available"; + return; + } + + qCDebug(dcBluetoothServer()) << "WirelessService: Using" << Loopd::instance()->networkManager()->wirelessNetworkDevices().first(); + m_device = Loopd::instance()->networkManager()->wirelessNetworkDevices().first(); + connect(m_device, &WirelessNetworkDevice::bitRateChanged, this, &WirelessService::onWirelessDeviceBitRateChanged); + connect(m_device, &WirelessNetworkDevice::stateChanged, this, &WirelessService::onWirelessDeviceStateChanged); +} + +QLowEnergyService *WirelessService::service() +{ + return m_service; +} + +QLowEnergyServiceData WirelessService::serviceData() +{ + QLowEnergyServiceData serviceData; + serviceData.setType(QLowEnergyServiceData::ServiceTypePrimary); + serviceData.setUuid(wirelessServiceUuid); + + QLowEnergyDescriptorData clientConfigDescriptorData(QBluetoothUuid::ClientCharacteristicConfiguration, QByteArray(2, 0)); + + // Wifi commander characterisitc e081fec1-f757-4449-b9c9-bfa83133f7fc + QLowEnergyCharacteristicData wirelessCommanderCharacteristicData; + wirelessCommanderCharacteristicData.setUuid(wirelessCommanderCharacteristicUuid); + wirelessCommanderCharacteristicData.setProperties(QLowEnergyCharacteristic::Write); + wirelessCommanderCharacteristicData.setValueLength(0, 20); + serviceData.addCharacteristic(wirelessCommanderCharacteristicData); + + // Response characterisitc e081fec2-f757-4449-b9c9-bfa83133f7fc + QLowEnergyCharacteristicData wirelessResponseCharacteristicData; + wirelessResponseCharacteristicData.setUuid(wirelessResponseCharacteristicUuid); + wirelessResponseCharacteristicData.setProperties(QLowEnergyCharacteristic::Notify); + wirelessResponseCharacteristicData.addDescriptor(clientConfigDescriptorData); + wirelessResponseCharacteristicData.setValueLength(0, 20); + serviceData.addCharacteristic(wirelessResponseCharacteristicData); + + // Wireless connection status characterisitc e081fec3-f757-4449-b9c9-bfa83133f7fc + QLowEnergyCharacteristicData wirelessStatusCharacteristicData; + wirelessStatusCharacteristicData.setUuid(wirelessStateCharacteristicUuid); + wirelessStatusCharacteristicData.setProperties(QLowEnergyCharacteristic::Read | QLowEnergyCharacteristic::Notify); + wirelessStatusCharacteristicData.addDescriptor(clientConfigDescriptorData); + wirelessStatusCharacteristicData.setValueLength(1, 1); + if (!Loopd::instance()->networkManager()->wirelessAvailable()) { + wirelessStatusCharacteristicData.setValue(WirelessService::getWirelessNetworkDeviceState(NetworkDevice::NetworkDeviceStateUnavailable)); + } else { + wirelessStatusCharacteristicData.setValue(WirelessService::getWirelessNetworkDeviceState(Loopd::instance()->networkManager()->wirelessNetworkDevices().first()->deviceState())); + } + serviceData.addCharacteristic(wirelessStatusCharacteristicData); + + return serviceData; +} + + +WirelessService::WirelessServiceResponse WirelessService::checkWirelessErrors() +{ + // Check possible errors + if (!Loopd::instance()->networkManager()->available()) { + qCWarning(dcBluetoothServer()) << "WirelessService: The networkmanager is not available."; + return WirelessServiceResponseNetworkManagerNotAvailable; + } + + if (!m_device) { + qCWarning(dcBluetoothServer()) << "WirelessService: There is no wireless device available."; + return WirelessServiceResponseWirelessNotAvailable; + } + + if (!Loopd::instance()->networkManager()->networkingEnabled()) { + qCWarning(dcBluetoothServer()) << "WirelessService: Networking not enabled"; + return WirelessServiceResponseNetworkingNotEnabled; + } + + if (!Loopd::instance()->networkManager()->wirelessEnabled()) { + qCWarning(dcBluetoothServer()) << "WirelessService: Wireless not enabled"; + return WirelessServiceResponseWirelessNotEnabled; + } + + return WirelessServiceResponseSuccess; +} + +QByteArray WirelessService::getWirelessNetworkDeviceState(const NetworkDevice::NetworkDeviceState &state) +{ + switch (state) { + case NetworkDevice::NetworkDeviceStateUnknown: + return QByteArray::fromHex("00"); + case NetworkDevice::NetworkDeviceStateUnmanaged: + return QByteArray::fromHex("01"); + case NetworkDevice::NetworkDeviceStateUnavailable: + return QByteArray::fromHex("02"); + case NetworkDevice::NetworkDeviceStateDisconnected: + return QByteArray::fromHex("03"); + case NetworkDevice::NetworkDeviceStatePrepare: + return QByteArray::fromHex("04"); + case NetworkDevice::NetworkDeviceStateConfig: + return QByteArray::fromHex("05"); + case NetworkDevice::NetworkDeviceStateNeedAuth: + return QByteArray::fromHex("06"); + case NetworkDevice::NetworkDeviceStateIpConfig: + return QByteArray::fromHex("07"); + case NetworkDevice::NetworkDeviceStateIpCheck: + return QByteArray::fromHex("08"); + case NetworkDevice::NetworkDeviceStateSecondaries: + return QByteArray::fromHex("09"); + case NetworkDevice::NetworkDeviceStateActivated: + return QByteArray::fromHex("0a"); + case NetworkDevice::NetworkDeviceStateDeactivating: + return QByteArray::fromHex("0b"); + case NetworkDevice::NetworkDeviceStateFailed: + return QByteArray::fromHex("0c"); + default: + qCWarning(dcBluetoothServer()) << "WirelessService: Unhandled wireless network device state." << state; + break; + } + + // Unknown + return QByteArray::fromHex("00"); +} + + +void WirelessService::streamData(const QVariantMap &responseMap) +{ + QLowEnergyCharacteristic characteristic = m_service->characteristic(wirelessResponseCharacteristicUuid); + if (!characteristic.isValid()) { + qCWarning(dcBluetoothServer()) << "WirelessService: Wireless response characteristic not valid"; + return; + } + + QByteArray data = QJsonDocument::fromVariant(responseMap).toJson(QJsonDocument::Compact) + '\n'; + qCDebug(dcBluetoothServer()) << "WirelessService: Start streaming response data:" << data.count() << "bytes"; + + int sentDataLength = 0; + QByteArray remainingData = data; + while (!remainingData.isEmpty()) { + QByteArray package = remainingData.left(20); + sentDataLength += package.count(); + m_service->writeCharacteristic(characteristic, package); + remainingData = remainingData.remove(0, package.count()); + } + + qCDebug(dcBluetoothServer()) << "WirelessService: Finished streaming response data"; +} + +QVariantMap WirelessService::createResponse(const WirelessService::WirelessServiceCommand &command, const WirelessService::WirelessServiceResponse &responseCode) +{ + QVariantMap response; + response.insert("c", (int)command); + response.insert("r", (int)responseCode); + return response; +} + +void WirelessService::commandGetNetworks(const QVariantMap &request) +{ + Q_UNUSED(request) + + if (!m_service) { + qCWarning(dcBluetoothServer()) << "WirelessService: Could not stream wireless network list. Service not valid"; + return; + } + + QLowEnergyCharacteristic characteristic = m_service->characteristic(wirelessResponseCharacteristicUuid); + if (!characteristic.isValid()) { + qCWarning(dcBluetoothServer()) << "WirelessService: Wireless response characteristic not valid"; + return; + } + + QVariantList accessPointVariantList; + foreach (WirelessAccessPoint *accessPoint, m_device->accessPoints()) { + QVariantMap accessPointVariantMap; + accessPointVariantMap.insert("e", accessPoint->ssid()); + accessPointVariantMap.insert("m", accessPoint->macAddress()); + accessPointVariantMap.insert("s", accessPoint->signalStrength()); + accessPointVariantMap.insert("p", (int)accessPoint->isProtected()); + accessPointVariantList.append(accessPointVariantMap); + } + + QVariantMap response = createResponse(WirelessServiceCommandGetNetworks); + response.insert("p", accessPointVariantList); + + streamData(response); +} + +void WirelessService::commandConnect(const QVariantMap &request) +{ + if (!m_service) { + qCWarning(dcBluetoothServer()) << "WirelessService: Could not stream wireless network list. Service not valid"; + return; + } + + QLowEnergyCharacteristic characteristic = m_service->characteristic(wirelessResponseCharacteristicUuid); + if (!characteristic.isValid()) { + qCWarning(dcBluetoothServer()) << "WirelessService: Wireless response characteristic not valid"; + return; + } + + if (!request.contains("p")) { + qCWarning(dcBluetoothServer()) << "WirelessService: Connect command: Missing parameters."; + streamData(createResponse(WirelessServiceCommandConnect, WirelessServiceResponseIvalidParameters)); + return; + } + + QVariantMap parameters = request.value("p").toMap(); + if (!parameters.contains("e") || !parameters.contains("p")) { + qCWarning(dcBluetoothServer()) << "WirelessService: Connect command: Invalid parameters."; + streamData(createResponse(WirelessServiceCommandConnect, WirelessServiceResponseIvalidParameters)); + return; + } + + Loopd::instance()->networkManager()->connectWifi(m_device->interface(), parameters.value("e").toString(), parameters.value("p").toString()); + streamData(createResponse(WirelessServiceCommandConnect)); +} + +void WirelessService::commandConnectHidden(const QVariantMap &request) +{ + Q_UNUSED(request) + // TODO: + qCWarning(dcBluetoothServer()) << "Connect to hidden network is not implemented yet."; +} + +void WirelessService::commandDisconnect(const QVariantMap &request) +{ + Q_UNUSED(request) + + if (!m_service) { + qCWarning(dcBluetoothServer()) << "WirelessService: Could not stream wireless network list. Service not valid"; + return; + } + + QLowEnergyCharacteristic characteristic = m_service->characteristic(wirelessResponseCharacteristicUuid); + if (!characteristic.isValid()) { + qCWarning(dcBluetoothServer()) << "WirelessService: Wireless response characteristic not valid"; + return; + } + + m_device->disconnectDevice(); + streamData(createResponse(WirelessServiceCommandDisconnect)); +} + +void WirelessService::commandScan(const QVariantMap &request) +{ + Q_UNUSED(request) + + if (!m_service) { + qCWarning(dcBluetoothServer()) << "WirelessService: Could not stream wireless network list. Service not valid"; + return; + } + + QLowEnergyCharacteristic characteristic = m_service->characteristic(wirelessResponseCharacteristicUuid); + if (!characteristic.isValid()) { + qCWarning(dcBluetoothServer()) << "WirelessService: Wireless response characteristic not valid"; + return; + } + + m_device->scanWirelessNetworks(); + streamData(createResponse(WirelessServiceCommandScan)); +} + +void WirelessService::commandGetCurrentConnection(const QVariantMap &request) +{ + Q_UNUSED(request) + + if (!m_service) { + qCWarning(dcBluetoothServer()) << "WirelessService: Could not stream wireless network list. Service not valid"; + return; + } + + QLowEnergyCharacteristic characteristic = m_service->characteristic(wirelessResponseCharacteristicUuid); + if (!characteristic.isValid()) { + qCWarning(dcBluetoothServer()) << "WirelessService: Wireless response characteristic not valid"; + return; + } + + QVariantMap connectionDataMap; + QNetworkInterface wifiInterface = QNetworkInterface::interfaceFromName(m_device->interface()); + if (!m_device->activeAccessPoint() || !wifiInterface.isValid() || wifiInterface.addressEntries().isEmpty()) { + qCDebug(dcBluetoothServer()) << "There is currently no access active accesspoint"; + connectionDataMap.insert("e", ""); + connectionDataMap.insert("m", ""); + connectionDataMap.insert("s", 0); + connectionDataMap.insert("p", 0); + connectionDataMap.insert("i", ""); + } else { + QHostAddress address = wifiInterface.addressEntries().first().ip(); + qCDebug(dcBluetoothServer()) << "Current connection:" << m_device->activeAccessPoint() << address.toString(); + connectionDataMap.insert("e", m_device->activeAccessPoint()->ssid()); + connectionDataMap.insert("m", m_device->activeAccessPoint()->macAddress()); + connectionDataMap.insert("s", m_device->activeAccessPoint()->signalStrength()); + connectionDataMap.insert("p", (int)m_device->activeAccessPoint()->isProtected()); + connectionDataMap.insert("i", address.toString()); + } + + QVariantMap response = createResponse(WirelessServiceCommandGetCurrentConnection); + response.insert("p", connectionDataMap); + streamData(response); +} + +void WirelessService::characteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value) +{ + // Command + if (characteristic.uuid() == wirelessCommanderCharacteristicUuid) { + // Check if currently reading + if (m_readingInputData) { + m_inputDataStream.append(value); + } else { + m_inputDataStream.clear(); + m_readingInputData = true; + m_inputDataStream.append(value); + } + + // If command finished + if (value.endsWith('\n')) { + QJsonParseError error; + QJsonDocument jsonDocument = QJsonDocument::fromJson(m_inputDataStream, &error); + if (error.error != QJsonParseError::NoError) { + qCWarning(dcBluetoothServer()) << "Got invalid json object" << m_inputDataStream; + m_inputDataStream.clear(); + m_readingInputData = false; + return; + } + + qCDebug(dcBluetoothServer()) << "Got command stream" << jsonDocument.toJson(); + + processCommand(jsonDocument.toVariant().toMap()); + + m_inputDataStream.clear(); + m_readingInputData = false; + } + + // Limit possible data stream to prevent overflow + if (m_inputDataStream.length() >= 20 * 1024) { + m_inputDataStream.clear(); + m_readingInputData = false; + return; + } + } +} + +void WirelessService::characteristicRead(const QLowEnergyCharacteristic &characteristic, const QByteArray &value) +{ + qCDebug(dcBluetoothServer()) << "WirelessService: Characteristic read" << characteristic.uuid().toString() << value; +} + +void WirelessService::characteristicWritten(const QLowEnergyCharacteristic &characteristic, const QByteArray &value) +{ + qCDebug(dcBluetoothServer()) << "WirelessService: Characteristic written" << characteristic.uuid().toString() << value; +} + +void WirelessService::descriptorRead(const QLowEnergyDescriptor &descriptor, const QByteArray &value) +{ + qCDebug(dcBluetoothServer()) << "WirelessService: Descriptor read" << descriptor.uuid().toString() << value; +} + +void WirelessService::descriptorWritten(const QLowEnergyDescriptor &descriptor, const QByteArray &value) +{ + qCDebug(dcBluetoothServer()) << "WirelessService: Descriptor written" << descriptor.uuid().toString() << value; +} + +void WirelessService::serviceError(const QLowEnergyService::ServiceError &error) +{ + QString errorString; + switch (error) { + case QLowEnergyService::NoError: + errorString = "No error"; + break; + case QLowEnergyService::OperationError: + errorString = "Operation error"; + break; + case QLowEnergyService::CharacteristicReadError: + errorString = "Characteristic read error"; + break; + case QLowEnergyService::CharacteristicWriteError: + errorString = "Characteristic write error"; + break; + case QLowEnergyService::DescriptorReadError: + errorString = "Descriptor read error"; + break; + case QLowEnergyService::DescriptorWriteError: + errorString = "Descriptor write error"; + break; + case QLowEnergyService::UnknownError: + errorString = "Unknown error"; + break; + default: + errorString = "Unhandled error"; + break; + } + + qCWarning(dcBluetoothServer()) << "WirelessService: Error:" << errorString; +} + +void WirelessService::processCommand(const QVariantMap &request) +{ + if (!request.contains("c")) { + qCWarning(dcBluetoothServer()) << "Invalid request. Command value missing."; + streamData(createResponse(WirelessServiceCommandConnect, WirelessServiceResponseIvalidCommand)); + return; + } + + bool commandIntOk; + int command = request.value("c").toInt(&commandIntOk); + if (!commandIntOk) { + qCWarning(dcBluetoothServer()) << "Invalid request. Could not convert method to interger."; + streamData(createResponse(WirelessServiceCommandConnect, WirelessServiceResponseIvalidCommand)); + return; + } + + // Check wireless errors + WirelessServiceResponse responseCode = checkWirelessErrors(); + if (responseCode != WirelessServiceResponseSuccess) { + streamData(createResponse((WirelessServiceCommand)command, responseCode)); + return; + } + + // Process method + switch (command) { + case WirelessServiceCommandGetNetworks: + commandGetNetworks(request); + break; + case WirelessServiceCommandConnect: + commandConnect(request); + break; + case WirelessServiceCommandConnectHidden: + commandConnectHidden(request); + break; + case WirelessServiceCommandDisconnect: + commandDisconnect(request); + break; + case WirelessServiceCommandScan: + commandScan(request); + break; + case WirelessServiceCommandGetCurrentConnection: + commandGetCurrentConnection(request); + break; + default: + qCWarning(dcBluetoothServer()) << "Invalid request. Unknown command" << command; + streamData(createResponse(WirelessServiceCommandConnect, WirelessServiceResponseIvalidCommand)); + break; + } +} + +void WirelessService::onWirelessDeviceBitRateChanged(const int &bitRate) +{ + Q_UNUSED(bitRate) +} + +void WirelessService::onWirelessDeviceStateChanged(const NetworkDevice::NetworkDeviceState &state) +{ + qCDebug(dcBluetoothServer()) << "WirelessService: Wireless network device state changed" << state; + + if (!m_service) { + qCWarning(dcBluetoothServer()) << "WirelessService: Could not update wireless network device state. Service not valid"; + return; + } + + QLowEnergyCharacteristic characteristic = m_service->characteristic(wirelessStateCharacteristicUuid); + if (!characteristic.isValid()) { + qCWarning(dcBluetoothServer()) << "WirelessService: Could not update wireless network device state. Characteristic not valid"; + return; + } + + m_service->writeCharacteristic(characteristic, WirelessService::getWirelessNetworkDeviceState(state)); +} diff --git a/nymea-networkmanager/bluetooth/wirelessservice.h b/nymea-networkmanager/bluetooth/wirelessservice.h new file mode 100644 index 0000000..03eb339 --- /dev/null +++ b/nymea-networkmanager/bluetooth/wirelessservice.h @@ -0,0 +1,101 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stürz * + * * + * This file is part of loopd. * + * * + * Loopd can not be copied and/or distributed without the express * + * permission of guh GmbH. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef WIRELESSSERVICE_H +#define WIRELESSSERVICE_H + +#include +#include +#include +#include + +#include "networkmanager/wirelessnetworkdevice.h" +#include "networkmanager/wirelessaccesspoint.h" + +class WirelessService : public QObject +{ + Q_OBJECT + Q_ENUMS(WirelessServiceCommand) + Q_ENUMS(WirelessServiceResponse) + +public: + + enum WirelessServiceCommand { + WirelessServiceCommandInvalid = -1, + WirelessServiceCommandGetNetworks = 0x00, + WirelessServiceCommandConnect = 0x01, + WirelessServiceCommandConnectHidden = 0x02, + WirelessServiceCommandDisconnect = 0x03, + WirelessServiceCommandScan = 0x04, + WirelessServiceCommandGetCurrentConnection = 0x05 + }; + Q_ENUM(WirelessServiceCommand) + + enum WirelessServiceResponse { + WirelessServiceResponseSuccess = 0x00, + WirelessServiceResponseIvalidCommand = 0x01, + WirelessServiceResponseIvalidParameters = 0x02, + WirelessServiceResponseNetworkManagerNotAvailable = 0x03, + WirelessServiceResponseWirelessNotAvailable = 0x04, + WirelessServiceResponseWirelessNotEnabled = 0x05, + WirelessServiceResponseNetworkingNotEnabled = 0x06, + WirelessServiceResponseUnknownError = 0x07 + }; + Q_ENUM(WirelessServiceResponse) + + explicit WirelessService(QLowEnergyService *service, QObject *parent = 0); + QLowEnergyService *service(); + + static QLowEnergyServiceData serviceData(); + +private: + QLowEnergyService *m_service; + WirelessNetworkDevice *m_device; + + bool m_readingInputData; + QByteArray m_inputDataStream; + + WirelessServiceResponse checkWirelessErrors(); + + // Note: static to be available in serviceData + static QByteArray getWirelessNetworkDeviceState(const NetworkDevice::NetworkDeviceState &state); + + void streamData(const QVariantMap &responseMap); + + QVariantMap createResponse(const WirelessServiceCommand &command, const WirelessServiceResponse &responseCode = WirelessServiceResponseSuccess); + + // Methods + void commandGetNetworks(const QVariantMap &request); + void commandConnect(const QVariantMap &request); + void commandConnectHidden(const QVariantMap &request); + void commandDisconnect(const QVariantMap &request); + void commandScan(const QVariantMap &request); + void commandGetCurrentConnection(const QVariantMap &request); + + +private slots: + // Service + void characteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value); + void characteristicRead(const QLowEnergyCharacteristic &characteristic, const QByteArray &value); + void characteristicWritten(const QLowEnergyCharacteristic &characteristic, const QByteArray &value); + void descriptorRead(const QLowEnergyDescriptor &descriptor, const QByteArray &value); + void descriptorWritten(const QLowEnergyDescriptor &descriptor, const QByteArray &value); + void serviceError(const QLowEnergyService::ServiceError &error); + + // Commands + void processCommand(const QVariantMap &request); + + // Wireless network device + void onWirelessDeviceBitRateChanged(const int &bitRate); + void onWirelessDeviceStateChanged(const NetworkDevice::NetworkDeviceState &state); +}; + +#endif // WIRELESSSERVICE_H diff --git a/nymea-networkmanager/core.cpp b/nymea-networkmanager/core.cpp new file mode 100644 index 0000000..7f40b90 --- /dev/null +++ b/nymea-networkmanager/core.cpp @@ -0,0 +1,29 @@ +#include "core.h" + +Core* Core::s_instance = 0; + +Core *Core::instance() +{ + if (!s_instance) + s_instance = new Core(); + + return s_instance; +} + +void Core::destroy() +{ + if (s_instance) + delete s_instance; + + s_instance = 0; +} + +Core::Core(QObject *parent) : + QObject(parent) +{ + + m_networkManager = new NetworkManager(this); + + + +} diff --git a/nymea-networkmanager/core.h b/nymea-networkmanager/core.h new file mode 100644 index 0000000..d54bf15 --- /dev/null +++ b/nymea-networkmanager/core.h @@ -0,0 +1,29 @@ +#ifndef CORE_H +#define CORE_H + +#include + +#include "networkmanager.h" + +class Core : public QObject +{ + Q_OBJECT +public: + static Core* instance(); + void destroy(); + + +private: + explicit Core(QObject *parent = nullptr); + static Core *s_instance; + + NetworkManager *m_networkManager = nullptr; + +signals: + +public slots: + + +}; + +#endif // CORE_H diff --git a/nymea-networkmanager/main.cpp b/nymea-networkmanager/main.cpp new file mode 100644 index 0000000..3236d8f --- /dev/null +++ b/nymea-networkmanager/main.cpp @@ -0,0 +1,12 @@ +#include + +#include "core.h" + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + + Core::instance(); + + return a.exec(); +} diff --git a/nymea-networkmanager/nymea-networkmanager.pro b/nymea-networkmanager/nymea-networkmanager.pro new file mode 100644 index 0000000..7d3f798 --- /dev/null +++ b/nymea-networkmanager/nymea-networkmanager.pro @@ -0,0 +1,31 @@ +include(../nymea-networkmanager.pri) + +TARGET = nymea-networkmanager + +QT += core network bluetooth dbus +QT -= gui + +CONFIG += console +CONFIG -= app_bundle + +TEMPLATE = app + +INCLUDEPATH += $$top_srcdir/libnymea-networkmanager/ +LIBS += -L$$top_builddir/libnymea-networkmanager/ -lnymea-networkmanager1 + +HEADERS += \ + core.h \ + bluetooth/bluetoothserver.h \ + bluetooth/networkservice.h \ + bluetooth/bluetoothuuids.h \ + bluetooth/wirelessservice.h \ + +SOURCES += \ + main.cpp \ + core.cpp \ + bluetooth/bluetoothserver.cpp \ + bluetooth/networkservice.cpp \ + bluetooth/wirelessservice.cpp \ + +target.path = /usr/bin +INSTALLS += target