diff --git a/libnymea-common/libnymea-common.pro b/libnymea-common/libnymea-common.pro index 4a7c42f8..8d2a38c7 100644 --- a/libnymea-common/libnymea-common.pro +++ b/libnymea-common/libnymea-common.pro @@ -9,26 +9,27 @@ CONFIG += static target.path = /usr/lib/$$system('dpkg-architecture -q DEB_HOST_MULTIARCH') INSTALLS += target -HEADERS += types/types.h \ - types/vendor.h \ - types/vendors.h \ - types/deviceclass.h \ - types/device.h \ - types/param.h \ - types/params.h \ - types/paramtype.h \ - types/paramtypes.h \ - types/statetype.h \ - types/statetypes.h \ - types/eventtype.h \ - types/eventtypes.h \ - types/actiontype.h \ - types/actiontypes.h \ - types/state.h \ - types/states.h \ - types/statesproxy.h \ - types/plugin.h \ - types/plugins.h \ +HEADERS += \ + types/types.h \ + types/vendor.h \ + types/vendors.h \ + types/deviceclass.h \ + types/device.h \ + types/param.h \ + types/params.h \ + types/paramtype.h \ + types/paramtypes.h \ + types/statetype.h \ + types/statetypes.h \ + types/eventtype.h \ + types/eventtypes.h \ + types/actiontype.h \ + types/actiontypes.h \ + types/state.h \ + types/states.h \ + types/statesproxy.h \ + types/plugin.h \ + types/plugins.h \ types/rules.h \ types/rule.h \ types/eventdescriptor.h \ @@ -51,25 +52,26 @@ HEADERS += types/types.h \ types/timeeventitems.h \ types/calendaritems.h -SOURCES += types/vendor.cpp \ - types/vendors.cpp \ - types/deviceclass.cpp \ - types/device.cpp \ - types/param.cpp \ - types/params.cpp \ - types/paramtype.cpp \ - types/paramtypes.cpp \ - types/statetype.cpp \ - types/statetypes.cpp \ - types/eventtype.cpp \ - types/eventtypes.cpp \ - types/actiontype.cpp \ - types/actiontypes.cpp \ - types/state.cpp \ - types/states.cpp \ - types/statesproxy.cpp \ - types/plugin.cpp \ - types/plugins.cpp \ +SOURCES += \ + types/vendor.cpp \ + types/vendors.cpp \ + types/deviceclass.cpp \ + types/device.cpp \ + types/param.cpp \ + types/params.cpp \ + types/paramtype.cpp \ + types/paramtypes.cpp \ + types/statetype.cpp \ + types/statetypes.cpp \ + types/eventtype.cpp \ + types/eventtypes.cpp \ + types/actiontype.cpp \ + types/actiontypes.cpp \ + types/state.cpp \ + types/states.cpp \ + types/statesproxy.cpp \ + types/plugin.cpp \ + types/plugins.cpp \ types/rules.cpp \ types/rule.cpp \ types/eventdescriptor.cpp \ diff --git a/libnymea-common/types/ruleactions.cpp b/libnymea-common/types/ruleactions.cpp index 845c03ee..4d6301b9 100644 --- a/libnymea-common/types/ruleactions.cpp +++ b/libnymea-common/types/ruleactions.cpp @@ -14,6 +14,8 @@ int RuleActions::rowCount(const QModelIndex &parent) const QVariant RuleActions::data(const QModelIndex &index, int role) const { + Q_UNUSED(index) + Q_UNUSED(role) return QVariant(); } diff --git a/libnymea-common/types/stateevaluators.cpp b/libnymea-common/types/stateevaluators.cpp index 73e15693..49d98ab3 100644 --- a/libnymea-common/types/stateevaluators.cpp +++ b/libnymea-common/types/stateevaluators.cpp @@ -13,6 +13,8 @@ int StateEvaluators::rowCount(const QModelIndex &parent) const QVariant StateEvaluators::data(const QModelIndex &index, int role) const { + Q_UNUSED(index) + Q_UNUSED(role) return QVariant(); } diff --git a/mea/engine.cpp b/mea/engine.cpp index ddbb4256..191224d1 100644 --- a/mea/engine.cpp +++ b/mea/engine.cpp @@ -68,6 +68,11 @@ BasicConfiguration *Engine::basicConfiguration() const return m_basicConfiguration; } +BluetoothDiscovery *Engine::bluetoothDiscovery() const +{ + return m_bluetoothDiscovery; +} + NymeaConnection *Engine::connection() const { return m_connection; @@ -80,7 +85,8 @@ Engine::Engine(QObject *parent) : m_deviceManager(new DeviceManager(m_jsonRpcClient, this)), m_ruleManager(new RuleManager(m_jsonRpcClient, this)), m_logManager(new LogManager(m_jsonRpcClient, this)), - m_basicConfiguration(new BasicConfiguration(m_jsonRpcClient, this)) + m_basicConfiguration(new BasicConfiguration(m_jsonRpcClient, this)), + m_bluetoothDiscovery(new BluetoothDiscovery(this)) { connect(m_jsonRpcClient, &JsonRpcClient::connectedChanged, this, &Engine::onConnectedChanged); connect(m_jsonRpcClient, &JsonRpcClient::authenticationRequiredChanged, this, &Engine::onConnectedChanged); diff --git a/mea/engine.h b/mea/engine.h index 4b476001..9610344b 100644 --- a/mea/engine.h +++ b/mea/engine.h @@ -28,6 +28,7 @@ #include "devicemanager.h" #include "nymeainterface.h" #include "jsonrpc/jsonrpcclient.h" +#include "wifisetup/bluetoothdiscovery.h" class RuleManager; class LogManager; @@ -41,6 +42,7 @@ class Engine : public QObject Q_PROPERTY(RuleManager* ruleManager READ ruleManager CONSTANT) Q_PROPERTY(JsonRpcClient* jsonRpcClient READ jsonRpcClient CONSTANT) Q_PROPERTY(BasicConfiguration* basicConfiguration READ basicConfiguration CONSTANT) + Q_PROPERTY(BluetoothDiscovery* bluetoothDiscovery READ bluetoothDiscovery CONSTANT) public: static Engine *instance(); @@ -54,7 +56,8 @@ public: RuleManager *ruleManager() const; JsonRpcClient *jsonRpcClient() const; LogManager *logManager() const; - BasicConfiguration* basicConfiguration() const; + BasicConfiguration *basicConfiguration() const; + BluetoothDiscovery *bluetoothDiscovery() const; private: explicit Engine(QObject *parent = 0); @@ -66,6 +69,7 @@ private: RuleManager *m_ruleManager; LogManager *m_logManager; BasicConfiguration *m_basicConfiguration; + BluetoothDiscovery *m_bluetoothDiscovery; private slots: void onConnectedChanged(); diff --git a/mea/main.cpp b/mea/main.cpp index dd2e3764..ad6bc048 100644 --- a/mea/main.cpp +++ b/mea/main.cpp @@ -52,6 +52,7 @@ #include "models/valuelogsproxymodel.h" #include "models/eventdescriptorparamsfiltermodel.h" #include "basicconfiguration.h" +#include "wifisetup/networkmanagercontroler.h" static QObject* interfacesModel_provider(QQmlEngine *engine, QJSEngine *scriptEngine) { @@ -156,6 +157,13 @@ int main(int argc, char *argv[]) qmlRegisterType(uri, 1, 0, "ValueLogsProxyModel"); qmlRegisterUncreatableType(uri, 1, 0, "LogEntry", "Get them from LogsModel"); + qmlRegisterType(uri, 1, 0, "NetworkManagerControler"); + qmlRegisterUncreatableType(uri, 1, 0, "BluetoothDiscovery", "Can't create this in QML. Get it from the Engine instance."); + qmlRegisterUncreatableType(uri, 1, 0, "BluetoothDeviceInfo", "Can't create this in QML. Get it from the DeviceInfos."); + qmlRegisterUncreatableType(uri, 1, 0, "BluetoothDeviceInfos", "Can't create this in QML. Get it from the BluetoothDiscovery."); + qmlRegisterUncreatableType(uri, 1, 0, "WirelessSetupManager", "Can't create this in QML. Get it from the NetworkManagerControler."); + qmlRegisterUncreatableType(uri, 1, 0, "WirelessAccesspoints", "Can't create this in QML. Get it from the Loop."); + Engine::instance(); QQmlApplicationEngine engine; diff --git a/mea/mea.pro b/mea/mea.pro index 5956ceff..98cdac20 100644 --- a/mea/mea.pro +++ b/mea/mea.pro @@ -2,8 +2,7 @@ TEMPLATE=app TARGET=mea include(../mea.pri) - -QT += qml quick quickcontrols2 websockets svg +QT += qml quick quickcontrols2 websockets svg bluetooth INCLUDEPATH += $$top_srcdir/libnymea-common LIBS += -L$$top_builddir/libnymea-common/release -L$$top_builddir/libnymea-common/ -lnymea-common @@ -38,7 +37,15 @@ HEADERS += engine.h \ discovery/nymeadiscovery.h \ logmanager.h \ basicconfiguration.h \ - models/eventdescriptorparamsfiltermodel.h + models/eventdescriptorparamsfiltermodel.h \ + wifisetup/bluetoothdevice.h \ + wifisetup/bluetoothdeviceinfo.h \ + wifisetup/bluetoothdeviceinfos.h \ + wifisetup/bluetoothdiscovery.h \ + wifisetup/wirelessaccesspoint.h \ + wifisetup/wirelessaccesspoints.h \ + wifisetup/wirelesssetupmanager.h \ + wifisetup/networkmanagercontroler.h SOURCES += main.cpp \ @@ -72,7 +79,15 @@ SOURCES += main.cpp \ discovery/nymeadiscovery.cpp \ logmanager.cpp \ basicconfiguration.cpp \ - models/eventdescriptorparamsfiltermodel.cpp + models/eventdescriptorparamsfiltermodel.cpp \ + wifisetup/bluetoothdevice.cpp \ + wifisetup/bluetoothdeviceinfo.cpp \ + wifisetup/bluetoothdeviceinfos.cpp \ + wifisetup/bluetoothdiscovery.cpp \ + wifisetup/wirelessaccesspoint.cpp \ + wifisetup/wirelessaccesspoints.cpp \ + wifisetup/wirelesssetupmanager.cpp \ + wifisetup/networkmanagercontroler.cpp withavahi { DEFINES += WITH_AVAHI diff --git a/mea/nymeaconnection.cpp b/mea/nymeaconnection.cpp index 44f89143..0a6491bc 100644 --- a/mea/nymeaconnection.cpp +++ b/mea/nymeaconnection.cpp @@ -62,6 +62,11 @@ QString NymeaConnection::url() const return m_currentUrl.toString(); } +QString NymeaConnection::hostAddress() const +{ + return m_currentUrl.host(); +} + void NymeaConnection::sendData(const QByteArray &data) { if (connected()) { diff --git a/mea/nymeaconnection.h b/mea/nymeaconnection.h index 749c7fe7..3e37f0c3 100644 --- a/mea/nymeaconnection.h +++ b/mea/nymeaconnection.h @@ -14,6 +14,7 @@ class NymeaConnection : public QObject Q_OBJECT Q_PROPERTY(bool connected READ connected NOTIFY connectedChanged) Q_PROPERTY(QString url READ url NOTIFY connectedChanged) + Q_PROPERTY(QString hostAddress READ hostAddress NOTIFY connectedChanged) public: explicit NymeaConnection(QObject *parent = nullptr); @@ -23,7 +24,9 @@ public: Q_INVOKABLE void acceptCertificate(const QByteArray &fingerprint); bool connected(); + QString url() const; + QString hostAddress() const; void sendData(const QByteArray &data); diff --git a/mea/resources.qrc b/mea/resources.qrc index cffcdcfb..734e6b88 100644 --- a/mea/resources.qrc +++ b/mea/resources.qrc @@ -158,5 +158,21 @@ styles/dark/Page.qml styles/marantec/ApplicationWindow.qml styles/maveo/ApplicationWindow.qml + ui/BluetoothDiscoveryPage.qml + ui/images/bluetooth.svg + ui/images/refresh.svg + ui/WirelessControlerPage.qml + ui/BluetoothLoadingPage.qml + ui/images/nm-signal-00.svg + ui/images/nm-signal-00-secure.svg + ui/images/nm-signal-25.svg + ui/images/nm-signal-25-secure.svg + ui/images/nm-signal-50.svg + ui/images/nm-signal-50-secure.svg + ui/images/nm-signal-75.svg + ui/images/nm-signal-75-secure.svg + ui/images/nm-signal-100.svg + ui/images/nm-signal-100-secure.svg + ui/images/network-vpn.svg diff --git a/mea/rulemanager.cpp b/mea/rulemanager.cpp index bae7769c..9fb8ec87 100644 --- a/mea/rulemanager.cpp +++ b/mea/rulemanager.cpp @@ -259,6 +259,8 @@ void RuleManager::parseRuleExitActions(const QVariantList &ruleActions, Rule *ru void RuleManager::parseTimeDescriptor(const QVariantMap &timeDescriptor, Rule *rule) { + Q_UNUSED(rule) + foreach (const QVariant &timeEventItemVariant, timeDescriptor.value("timeEventItems").toList()) { TimeEventItem *timeEventItem = new TimeEventItem(); timeEventItem->setDateTime(QDateTime::fromSecsSinceEpoch(timeEventItemVariant.toMap().value("datetime").toULongLong())); diff --git a/mea/ui/BluetoothDiscoveryPage.qml b/mea/ui/BluetoothDiscoveryPage.qml new file mode 100644 index 00000000..3b13e4e5 --- /dev/null +++ b/mea/ui/BluetoothDiscoveryPage.qml @@ -0,0 +1,81 @@ +import QtQuick 2.4 +import QtQuick.Controls 2.1 +import QtQuick.Layouts 1.2 + +import "components" +import Mea 1.0 + + +Page { + id: root + header: GuhHeader { + text: qsTr("Bluetooth discovery") + onBackPressed: pageStack.pop() + + HeaderButton { + imageSource: Qt.resolvedUrl("images/refresh.svg") + onClicked: Engine.bluetoothDiscovery.start() + } + } + + Component.onCompleted: Engine.bluetoothDiscovery.start() + + ColumnLayout { + anchors.fill: parent + + BusyIndicator { + Layout.alignment: Qt.AlignHCenter + running: Engine.bluetoothDiscovery.discovering + } + + ThinDivider { } + + ListView { + Layout.fillWidth: true + Layout.fillHeight: true + + model: Engine.bluetoothDiscovery.deviceInfos + clip: true + + delegate: ItemDelegate { + width: parent.width + height: app.delegateHeight + + RowLayout { + anchors.verticalCenter: parent.verticalCenter + + Item { + Layout.fillHeight: true + Layout.preferredWidth: height + ColorIcon { + id: image + name: Qt.resolvedUrl("images/bluetooth.svg") + anchors.fill: parent + anchors.margins: app.margins / 2 + } + } + + ColumnLayout { + Layout.fillWidth: true + Layout.fillHeight: true + anchors.margins: app.margins + Label { + text: model.name + } + Label { + text: model.address + font.pixelSize: app.smallFont + } + } + } + + onClicked: { + print("Start bluetooth connection to", model.name, " --> ", model.address) + Engine.bluetoothDiscovery.stop() + pageStack.push(Qt.resolvedUrl("BluetoothLoadingPage.qml"), { name: model.name, address: model.address } ) + } + } + } + } + +} diff --git a/mea/ui/BluetoothLoadingPage.qml b/mea/ui/BluetoothLoadingPage.qml new file mode 100644 index 00000000..6b8a30bb --- /dev/null +++ b/mea/ui/BluetoothLoadingPage.qml @@ -0,0 +1,67 @@ +import QtQuick 2.4 +import QtQuick.Controls 2.1 +import QtQuick.Layouts 1.2 + +import "components" +import Mea 1.0 + + +Page { + id: root + + property string name + property string address + + NetworkManagerControler { + id: networkManger + name: root.name + address: root.address + + Component.onCompleted: networkManger.connectDevice() + } + + Connections { + target: networkManger.manager + onInitializedChanged: { + if (networkManger.manager.initialized) { + pageStack.push(Qt.resolvedUrl("WirelessControlerPage.qml"), { name: root.name, address: root.address, networkManger: networkManger } ) + } else { + pageStack.pop() + } + } + + onConnectedChanged: { + if (!networkManger.manager.connected) { + pageStack.pop() + } + } + } + + ColumnLayout { + anchors.centerIn: parent + + Label { + wrapMode: Text.WordWrap + font.pixelSize: app.largeFont + Layout.fillWidth: true + text: qsTr("Establish bluetooth LE connection") + } + + BusyIndicator { + Layout.alignment: Qt.AlignHCenter + running: true + } + + Label { + id: workingMessage + Layout.alignment: Qt.AlignHCenter + text: networkManger.manager.statusText + } + + Label { + id: initializingMessage + Layout.alignment: Qt.AlignHCenter + text: networkManger.manager.initializing ? qsTr("Initialize services...") : "" + } + } +} diff --git a/mea/ui/ConnectPage.qml b/mea/ui/ConnectPage.qml index b79223a2..7ff1e5ef 100644 --- a/mea/ui/ConnectPage.qml +++ b/mea/ui/ConnectPage.qml @@ -10,6 +10,35 @@ Page { readonly property bool haveHosts: discovery.discoveryModel.count > 0 + header: GuhHeader { + text: qsTr("Connect nymea") + backButtonVisible: false + menuButtonVisible: true + onMenuPressed: connectionMenu.open() + } + + Menu { + id: connectionMenu + width: implicitWidth + app.margins + + IconMenuItem { + iconSource: "../images/network-vpn.svg" + text: qsTr("Manual connect") + onTriggered: pageStack.push(manualConnectPage) + } + + MenuSeparator {} + + IconMenuItem { + iconSource: "../images/bluetooth.svg" + text: qsTr("Wireless setup") + onTriggered: pageStack.push(Qt.resolvedUrl("BluetoothDiscoveryPage.qml")) + } + + + + } + Component.onCompleted: { print("completed connectPage. last connected host:", settings.lastConnectedHost) if (settings.lastConnectedHost.length > 0) { @@ -43,8 +72,8 @@ Page { Label { Layout.fillWidth: true - text: root.haveHosts ? "Oh, look!" : "Uh oh" - color: "black" + text: root.haveHosts ? qsTr("Oh, look!") : qsTr("Uh oh") + //color: "black" font.pixelSize: app.largeFont } @@ -84,7 +113,7 @@ Page { } } onClicked: { - print("should connect to", model.nymeaRpcUrl) + print("Should connect to", model.nymeaRpcUrl) Engine.connection.connect(model.nymeaRpcUrl) pageStack.push(connectingPage) } @@ -119,10 +148,109 @@ Page { visible: root.haveHosts Label { Layout.fillWidth: true - text: "Not the ones you're looking for? We're looking for more!" + text: qsTr("Not the ones you're looking for? We're looking for more!") wrapMode: Text.WordWrap } - BusyIndicator { + + BusyIndicator { } + } + } + + Component { + id: manualConnectPage + + Page { + + header: GuhHeader { + text: qsTr("Manual connect to nymea") + onBackPressed: pageStack.pop() + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: app.margins + spacing: app.margins + + GridLayout { + Layout.fillHeight: true + Layout.fillWidth: true + + columns: 2 + + ComboBox { + id: connectionTypeComboBox + Layout.fillWidth: true + Layout.columnSpan: 2 + model: [ qsTr("TCP"), qsTr("Websocket") ] + } + + Label { text: qsTr("Address:") } + TextField { + id: addressTextInput + Layout.fillWidth: true + placeholderText: "127.0.0.1" + validator: RegExpValidator { regExp: /^((?:[0-1]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.){0,3}(?:[0-1]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$/ } + } + + Label { text: qsTr("Port:") } + TextField { + id: portTextInput + Layout.fillWidth: true + placeholderText: connectionTypeComboBox.currentIndex === 0 ? "2222" : "4444" + validator: IntValidator{bottom: 1; top: 65535;} + } + + Label { + Layout.fillWidth: true + text: qsTr("Encrypted connection:") + } + CheckBox { + id: secureCheckBox + checked: true + } + } + + + Button { + text: qsTr("Connect") + Layout.fillWidth: true + onClicked: { + var rpcUrl + var hostAddress + var port + + // Set default to placeholder + if (addressTextInput.text === "") { + hostAddress = addressTextInput.placeholderText + } else { + hostAddress = addressTextInput.text + } + + if (portTextInput.text === "") { + port = portTextInput.placeholderText + } else { + port = portTextInput.text + } + + if (connectionTypeComboBox.currentIndex == 0) { + if (secureCheckBox.checked) { + rpcUrl = "nymeas://" + hostAddress + ":" + port + } else { + rpcUrl = "nymea://" + hostAddress + ":" + port + } + } else if (connectionTypeComboBox.currentIndex == 1) { + if (secureCheckBox.checked) { + rpcUrl = "wss://" + hostAddress + ":" + port + } else { + rpcUrl = "ws://" + hostAddress + ":" + port + } + } + + print("Try to connect ", rpcUrl) + Engine.connection.connect(rpcUrl) + pageStack.push(connectingPage) + } + } } } } @@ -143,7 +271,7 @@ Page { } Button { - text: "Cancel" + text: qsTr("Cancel") Layout.fillWidth: true onClicked: { Engine.connection.disconnect() @@ -191,13 +319,13 @@ Page { Label { Layout.fillWidth: true wrapMode: Text.WordWrap - text: "The authenticity of this nymea box cannot be verified." + text: qsTr("The authenticity of this nymea box cannot be verified.") } Label { Layout.fillWidth: true wrapMode: Text.WordWrap - text: "If this is the first time you connect to this box, this is expected. Once you trust a box, you should never see this message again for that one. If you see this message multiple times for the same box, something suspicious is going on!" + text: qsTr("If this is the first time you connect to this box, this is expected. Once you trust a box, you should never see this message again for that one. If you see this message multiple times for the same box, something suspicious is going on!") } GridLayout { @@ -217,13 +345,13 @@ Page { Label { Layout.fillWidth: true wrapMode: Text.WrapAtWordBoundaryOrAnywhere - text: "Fingerprint: " + certDialog.fingerprint + text: qsTr("Fingerprint: ") + certDialog.fingerprint } Label { Layout.fillWidth: true wrapMode: Text.WordWrap - text: "Do you want to trust this device?" + text: qsTr("Do you want to trust this device?") font.bold: true } } diff --git a/mea/ui/EditDevicesPage.qml b/mea/ui/EditDevicesPage.qml index 71bb0ffd..d19fa71e 100644 --- a/mea/ui/EditDevicesPage.qml +++ b/mea/ui/EditDevicesPage.qml @@ -7,7 +7,7 @@ import Mea 1.0 Page { id: root header: GuhHeader { - text: "Configure Things" + text: qsTr("Configure Things") onBackPressed: pageStack.pop() } diff --git a/mea/ui/LoginPage.qml b/mea/ui/LoginPage.qml index 39783abf..6bb940d7 100644 --- a/mea/ui/LoginPage.qml +++ b/mea/ui/LoginPage.qml @@ -9,7 +9,7 @@ Page { signal backPressed(); header: GuhHeader { - text: "Welcome to nymea!" + text: qsTr("Welcome to nymea!") backButtonVisible: true onBackPressed: root.backPressed() } @@ -49,8 +49,8 @@ Page { Label { Layout.fillWidth: true text: Engine.jsonRpcClient.initialSetupRequired ? - "In order to use your nymea system, please enter your email address and set a password for your nymea box." - : "In order to use your nymea system, please log in." + qsTr("In order to use your nymea system, please enter your email address and set a password for your nymea box.") + : qsTr("In order to use your nymea system, please log in.") wrapMode: Text.WordWrap } @@ -58,7 +58,7 @@ Page { Layout.fillWidth: true Label { - text: "Your e-mail address:" + text: qsTr("Your e-mail address:") Layout.fillWidth: true } TextField { @@ -73,7 +73,7 @@ Page { Label { Layout.fillWidth: true - text: "Password:" + text: qsTr("Password:") } TextField { id: passwordTextField @@ -88,7 +88,7 @@ Page { Label { Layout.fillWidth: true - text: "Confirm password:" + text: qsTr("Confirm password:") } TextField { id: confirmPasswordTextField @@ -110,7 +110,7 @@ Page { Button { Layout.fillWidth: true - text: "OK" + text: qsTr("OK") enabled: usernameTextField.text.length >= 5 && passwordTextField.text.length >= 8 && (!Engine.jsonRpcClient.initialSetupRequired || confirmPasswordTextField.text == passwordTextField.text) onClicked: { diff --git a/mea/ui/MainPage.qml b/mea/ui/MainPage.qml index 1101816b..ec8d006f 100644 --- a/mea/ui/MainPage.qml +++ b/mea/ui/MainPage.qml @@ -9,7 +9,7 @@ Page { id: root header: GuhHeader { - text: "My things" + text: qsTr("My things") backButtonVisible: false menuButtonVisible: true onMenuPressed: mainMenu.open() @@ -40,30 +40,30 @@ Page { width: implicitWidth + app.margins IconMenuItem { iconSource: "../images/share.svg" - text: "Configure things" + text: qsTr("Configure things") onTriggered: pageStack.push(Qt.resolvedUrl("EditDevicesPage.qml")) } IconMenuItem { iconSource: "../images/add.svg" - text: "Add a new thing" + text: qsTr("Add a new thing") onTriggered: pageStack.push(Qt.resolvedUrl("NewDeviceWizard.qml")) } MenuSeparator {} IconMenuItem { iconSource: "../images/magic.svg" - text: "Magic" + text: qsTr("Magic") onTriggered: pageStack.push(Qt.resolvedUrl("MagicPage.qml")) } MenuSeparator {} IconMenuItem { iconSource: "../images/settings.svg" - text: "Settings" + text: qsTr("Settings") onTriggered: pageStack.push(Qt.resolvedUrl("SettingsPage.qml")) } MenuSeparator {} IconMenuItem { iconSource: "../images/info.svg" - text: "System information" + text: qsTr("System information") onTriggered: pageStack.push(Qt.resolvedUrl("SystemInfoPage.qml")) } } @@ -137,7 +137,7 @@ Page { running: parent.visible } Label { - text: "Loading data..." + text: qsTr("Loading data...") font.pixelSize: app.largeFont Layout.fillWidth: true wrapMode: Text.WordWrap @@ -150,7 +150,7 @@ Page { spacing: app.margins visible: Engine.deviceManager.devices.count === 0 && !Engine.deviceManager.fetchingData Label { - text: "Welcome to nymea!" + text: qsTr("Welcome to nymea!") font.pixelSize: app.largeFont Layout.fillWidth: true wrapMode: Text.WordWrap @@ -158,7 +158,7 @@ Page { color: app.guhAccent } Label { - text: "There are no things set up yet. You can start with adding your things by using the menu on the upper left and selecting \"Add a new thing\"." + text: qsTr("There are no things set up yet. You can start with adding your things by using the menu on the upper left and selecting \"Add a new thing\".") Layout.fillWidth: true wrapMode: Text.WordWrap horizontalAlignment: Text.AlignHCenter diff --git a/mea/ui/NewDeviceWizard.qml b/mea/ui/NewDeviceWizard.qml index 8ad6fa80..06774f1e 100644 --- a/mea/ui/NewDeviceWizard.qml +++ b/mea/ui/NewDeviceWizard.qml @@ -9,7 +9,7 @@ Page { id: root header: GuhHeader { - text: "Set up new thing" + text: qsTr("Set up new thing") backButtonVisible: internalPageStack.depth > 1 onBackPressed: { internalPageStack.pop(); @@ -246,7 +246,7 @@ Page { visible: discovery.busy spacing: app.margins * 2 Label { - text: "Searching for things..." + text: qsTr("Searching for things...") Layout.fillWidth: true font.pixelSize: app.largeFont horizontalAlignment: Text.AlignHCenter @@ -264,23 +264,23 @@ Page { visible: !discovery.busy && discovery.count == 0 spacing: app.margins * 2 Label { - text: "Too bad..." + text: qsTr("Too bad...") font.pixelSize: app.largeFont Layout.fillWidth: true } Label { - text: "No things of this kind could be found..." + text: qsTr("No things of this kind could be found...") Layout.fillWidth: true wrapMode: Text.WordWrap } Label { Layout.fillWidth: true - text: "Make sure your things are set up and connected, try searching again or go back and pick a different kind of thing." + text: qsTr("Make sure your things are set up and connected, try searching again or go back and pick a different kind of thing.") wrapMode: Text.WordWrap } Button { - text: "Try again!" + text: qsTr("Try again!") Layout.fillWidth: true onClicked: { discovery.discoverDevices(d.deviceClass.id, d.discoveryParams) diff --git a/mea/ui/PushButtonAuthPage.qml b/mea/ui/PushButtonAuthPage.qml index b78fc767..ec3e3d4e 100644 --- a/mea/ui/PushButtonAuthPage.qml +++ b/mea/ui/PushButtonAuthPage.qml @@ -9,7 +9,7 @@ Page { signal backPressed(); header: GuhHeader { - text: "Welcome to nymea!" + text: qsTr("Welcome to nymea!") backButtonVisible: true onBackPressed: { root.backPressed(); @@ -58,7 +58,7 @@ Page { Label { Layout.fillWidth: true - text: "Please press the button on your nymea box to authenticate this device." + text: qsTr("Please press the button on your nymea box to authenticate this device.") wrapMode: Text.WordWrap } diff --git a/mea/ui/SettingsPage.qml b/mea/ui/SettingsPage.qml index 99b2cc4a..5290cac6 100644 --- a/mea/ui/SettingsPage.qml +++ b/mea/ui/SettingsPage.qml @@ -8,7 +8,7 @@ import "components" Page { id: root header: GuhHeader { - text: "Settings" + text: qsTr("Settings") backButtonVisible: true onBackPressed: pageStack.pop() } @@ -21,7 +21,7 @@ Page { Layout.margins: app.margins Label { - text: "Application".toUpperCase() + text: qsTr("Application").toUpperCase() color: app.guhAccent Layout.fillWidth: true } @@ -30,10 +30,10 @@ Page { Layout.fillWidth: true Label { Layout.fillWidth: true - text: "View mode" + text: qsTr("View mode") } ComboBox { - model: ["Windowed", "Maximized", "Fullscreen"] + model: [qsTr("Windowed"), qsTr("Maximized"), qsTr("Fullscreen")] currentIndex: { switch (settings.viewMode) { case ApplicationWindow.Windowed: @@ -90,7 +90,7 @@ Page { Layout.fillWidth: true Label { Layout.fillWidth: true - text: "Return to home on idle" + text: qsTr("Return to home on idle") } CheckBox { checked: settings.returnToHome @@ -101,16 +101,16 @@ Page { Layout.fillWidth: true Label { Layout.fillWidth: true - text: "Graph style" + text: qsTr("Graph style") } RadioButton { checked: settings.graphStyle === "bars" - text: "Bars" + text: qsTr("Bars") onClicked: settings.graphStyle = "bars" } RadioButton { checked: settings.graphStyle === "bezier" - text: "Lines" + text: qsTr("Lines") onClicked: settings.graphStyle = "bezier" } @@ -125,7 +125,7 @@ Page { Layout.leftMargin: app.margins Layout.rightMargin: app.margins Layout.topMargin: app.margins - text: "System".toUpperCase() + text: qsTr("System").toUpperCase() color: app.guhAccent } @@ -158,10 +158,20 @@ Page { Layout.fillWidth: true } Switch { + id: debugServerEnabledSwitch checked: Engine.basicConfiguration.debugServerEnabled onClicked: Engine.basicConfiguration.debugServerEnabled = checked } } + + Button { + id: debugServerButton + Layout.fillWidth: true + visible: debugServerEnabledSwitch.checked + text: qsTr("Open debug interface") + onClicked: Qt.openUrlExternally("http://" + Engine.connection.hostAddress + "/debug") + } + } ItemDelegate { @@ -169,7 +179,7 @@ Page { contentItem: RowLayout { Label { Layout.fillWidth: true - text: "Plugins" + text: qsTr("Plugins") } Image { source: "images/next.svg" diff --git a/mea/ui/SystemInfoPage.qml b/mea/ui/SystemInfoPage.qml index 15a99913..1c8fab13 100644 --- a/mea/ui/SystemInfoPage.qml +++ b/mea/ui/SystemInfoPage.qml @@ -8,7 +8,7 @@ import Mea 1.0 Page { id: root header: GuhHeader { - text: "System information" + text: qsTr("System information") backButtonVisible: true onBackPressed: pageStack.pop() } @@ -22,7 +22,7 @@ Page { Label { Layout.fillWidth: true - text: "Connected to:" + text: qsTr("Connected to:") color: Material.accent } RowLayout { @@ -33,7 +33,7 @@ Page { text: Engine.connection.url } Button { - text: "Disconnect" + text: qsTr("Disconnect") onClicked: { settings.lastConnectedHost = ""; Engine.connection.disconnect(); @@ -48,7 +48,7 @@ Page { Layout.fillWidth: true contentItem: RowLayout { Label { - text: "Log viewer" + text: qsTr("Log viewer") Layout.fillWidth: true } Image { diff --git a/mea/ui/WirelessControlerPage.qml b/mea/ui/WirelessControlerPage.qml new file mode 100644 index 00000000..8288222e --- /dev/null +++ b/mea/ui/WirelessControlerPage.qml @@ -0,0 +1,310 @@ +import QtQuick 2.4 +import QtQuick.Controls 2.1 +import QtQuick.Layouts 1.2 + +import "components" +import Mea 1.0 + +Page { + id: root + + property string name + property string address + property QtObject networkManger + + header: GuhHeader { + text: qsTr("Wireless network") + onBackPressed: { + pageStack.pop() + pageStack.pop() + } + + HeaderButton { + imageSource: Qt.resolvedUrl("images/refresh.svg") + onClicked: networkManger.manager.loadNetworks() + } + + HeaderButton { + imageSource: Qt.resolvedUrl("images/settings.svg") + onClicked: pageStack.push(settingsPage) + } + + } + + Component.onCompleted: networkManger.manager.loadNetworks() + + ColumnLayout { + anchors.fill: parent + visible: networkManger.manager.initialized + + Label { + wrapMode: Text.WordWrap + Layout.fillWidth: true + text: qsTr("Network status: ") + networkManger.manager.networkStatus + } + + + Label { + wrapMode: Text.WordWrap + Layout.fillWidth: true + text: qsTr("Wireless status: ") + networkManger.manager.wirelessStatus + } + + BusyIndicator { + Layout.alignment: Qt.AlignHCenter + running: networkManger.manager.working + } + + ThinDivider { } + + ListView { + Layout.fillWidth: true + Layout.fillHeight: true + + model: networkManger.manager.accessPoints + clip: true + + delegate: ItemDelegate { + width: parent.width + height: app.delegateHeight + + RowLayout { + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.right: parent.right + + Item { + Layout.fillHeight: true + Layout.preferredWidth: height + + ColorIcon { + id: image + anchors.fill: parent + anchors.margins: app.margins / 2 + name: { + if (model.protected) { + if (model.signalStrength <= 25) + return Qt.resolvedUrl("images/nm-signal-25-secure.svg") + + if (model.signalStrength <= 50) + return Qt.resolvedUrl("images/nm-signal-50-secure.svg") + + if (model.signalStrength <= 75) + return Qt.resolvedUrl("images/nm-signal-75-secure.svg") + + if (model.signalStrength <= 100) + return Qt.resolvedUrl("images/nm-signal-100-secure.svg") + + } else { + + if (model.signalStrength <= 25) + return Qt.resolvedUrl("images/nm-signal-25.svg") + + if (model.signalStrength <= 50) + return Qt.resolvedUrl("images/nm-signal-50.svg") + + if (model.signalStrength <= 75) + return Qt.resolvedUrl("images/nm-signal-75.svg") + + if (model.signalStrength <= 100) + return Qt.resolvedUrl("images/nm-signal-100.svg") + + } + } + } + } + + Label { + Layout.alignment: Qt.AlignVCenter + text: model.signalStrength + "%" + } + + ColumnLayout { + Layout.fillWidth: true + + Label { + text: model.ssid + } + + Label { + text: model.macAddress + font.pixelSize: app.smallFont + } + } + } + + onClicked: { + print("Connect to ", model.ssid, " --> ", model.macAddress) + pageStack.push(authenticationPage, { ssid: model.ssid, macAddress: model.macAddress }) + } + } + } + } + + Component { + id: authenticationPage + + Page { + id: root + + property string ssid + property string macAddress + + header: GuhHeader { + text: qsTr("Wireless authentication") + onBackPressed: pageStack.pop() + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: app.margins + + Label { + wrapMode: Text.WordWrap + font.pixelSize: app.largeFont + Layout.fillWidth: true + text: ssid + " (" + macAddress + ")" + } + + Label { + wrapMode: Text.WordWrap + Layout.fillWidth: true + text: qsTr("Please enter the password for the Wifi network.") + } + + TextField { + Layout.fillWidth: true + id: passwordTextField + echoMode: TextInput.Password + } + + Button { + Layout.fillWidth: true + text: qsTr("Connect") + onPressed: { + networkManger.manager.connectWirelessNetwork(ssid, passwordTextField.text) + pageStack.pop() + } + } + + } + } + } + + Component { + id: settingsPage + + Page { + id: root + header: GuhHeader { + text: qsTr("Network manager settings") + onBackPressed: pageStack.pop() + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: app.margins + + + RowLayout { + anchors.margins: app.margins + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + text: qsTr("Networking") + } + + Switch { + id: networkingSwitch + checked: networkManger.manager.networkingEnabled + onCheckedChanged: networkManger.manager.enableNetworking(checked) + } + } + + RowLayout { + anchors.margins: app.margins + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + text: qsTr("Wireless networking") + } + + Switch { + id: wirelessNetworkingSwitch + checked: networkManger.manager.wirelessEnabled + onCheckedChanged: networkManger.manager.enableWireless(checked) + } + } + + ThinDivider { } + + RowLayout { + anchors.margins: app.margins + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + text: qsTr("System UUID") + } + + Label { + text: networkManger.manager.modelNumber + } + } + + RowLayout { + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + text: qsTr("Manufacturer") + } + Label { + text: networkManger.manager.manufacturer + } + } + + RowLayout { + anchors.margins: app.margins + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + text: qsTr("Software revision") + } + Label { + text: networkManger.manager.softwareRevision + } + } + + RowLayout { + anchors.margins: app.margins + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + text: qsTr("Firmware revision") + } + Label { + text: networkManger.manager.firmwareRevision + } + } + + RowLayout { + anchors.margins: app.margins + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + text: qsTr("Hardware revision") + } + Label { + text: networkManger.manager.hardwareRevision + } + } + } + } + } +} diff --git a/mea/ui/actiondelegates/ActionDelegateFallback.qml b/mea/ui/actiondelegates/ActionDelegateFallback.qml index f57c21c3..3d643ada 100644 --- a/mea/ui/actiondelegates/ActionDelegateFallback.qml +++ b/mea/ui/actiondelegates/ActionDelegateFallback.qml @@ -16,7 +16,7 @@ ActionDelegateBase { } Label { Layout.fillWidth: true - text: "Note: This action type has not been implemented yet" + text: qsTr("Note: This action type has not been implemented yet") font.pixelSize: app.smallFont } } diff --git a/mea/ui/actiondelegates/ActionDelegateNoParams.qml b/mea/ui/actiondelegates/ActionDelegateNoParams.qml index 97fed6c2..bcec8ab0 100644 --- a/mea/ui/actiondelegates/ActionDelegateNoParams.qml +++ b/mea/ui/actiondelegates/ActionDelegateNoParams.qml @@ -14,7 +14,7 @@ ActionDelegateBase { Layout.fillWidth: true } Button { - text: "Do it!" + text: qsTr("Do it!") onClicked: root.executeAction([]) } } diff --git a/mea/ui/actiondelegates/ActionDelegateStringFromStringList.qml b/mea/ui/actiondelegates/ActionDelegateStringFromStringList.qml index 267ff7f5..a71cb3d5 100644 --- a/mea/ui/actiondelegates/ActionDelegateStringFromStringList.qml +++ b/mea/ui/actiondelegates/ActionDelegateStringFromStringList.qml @@ -18,7 +18,7 @@ ActionDelegateBase { model: root.paramType.allowedValues currentIndex: root.paramType.allowedValues.indexOf(root.actionState) onActivated: { - if (root.actionState == root.paramType.allowedValues[index]) { + if (root.actionState === root.paramType.allowedValues[index]) { return; } diff --git a/mea/ui/components/Graph.qml b/mea/ui/components/Graph.qml index 8982944a..f0725e17 100644 --- a/mea/ui/components/Graph.qml +++ b/mea/ui/components/Graph.qml @@ -20,7 +20,7 @@ Item { anchors.centerIn: parent width: parent.width - 2 * app.margins wrapMode: Text.WordWrap - text: "Sorry, there isn't enough data to display a graph here yet!" + text: qsTr("Sorry, there isn't enough data to display a graph here yet!") visible: !root.model.busy && root.model.count <= 2 horizontalAlignment: Text.AlignHCenter font.pixelSize: app.largeFont diff --git a/mea/ui/components/InterfacesModels.qml b/mea/ui/components/InterfacesModels.qml index 4ed7b794..516d5740 100644 --- a/mea/ui/components/InterfacesModels.qml +++ b/mea/ui/components/InterfacesModels.qml @@ -2,7 +2,7 @@ import QtQuick 2.0 Item { property ListModel eventTemplateModel: ListModel { - ListElement { interfaceName: "battery"; stateName: "batteryLevel"; stateDisplayName: "Battery level"; eventDisplayName: "Battery level changed" } - ListElement { interfaceName: "battery"; stateName: "batteryCritical"; stateDisplayName: "Battery critical"; eventDisplayName: "Battery critical changed" } + ListElement { interfaceName: "battery"; stateName: "batteryLevel"; stateDisplayName: qsTr("Battery level"); eventDisplayName: qsTr("Battery level changed") } + ListElement { interfaceName: "battery"; stateName: "batteryCritical"; stateDisplayName: qsTr("Battery critical"); eventDisplayName: qsTr("Battery critical changed") } } } diff --git a/mea/ui/customviews/GenericTypeLogView.qml b/mea/ui/customviews/GenericTypeLogView.qml index be291101..172adec0 100644 --- a/mea/ui/customviews/GenericTypeLogView.qml +++ b/mea/ui/customviews/GenericTypeLogView.qml @@ -91,7 +91,7 @@ Item { var matching = true; for (var k = 0; k < eventDescriptor.paramDescriptors.count; k++) { var paramDescriptor = eventDescriptor.paramDescriptors.get(k); - if (paramDescriptor.value == model.value) { + if (paramDescriptor.value === model.value) { return app.guhAccent; } } diff --git a/mea/ui/customviews/NotificationsView.qml b/mea/ui/customviews/NotificationsView.qml index 39be6b70..9f3c75e5 100644 --- a/mea/ui/customviews/NotificationsView.qml +++ b/mea/ui/customviews/NotificationsView.qml @@ -13,21 +13,21 @@ CustomViewBase { anchors { left: parent.left; top: parent.top; right: parent.right; margins: app.margins } Label { Layout.fillWidth: true - text: "Send a notification now:" + text: qsTr("Send a notification now:") } TextArea { id: titleTextArea - placeholderText: "Title" + placeholderText: qsTr("Title") Layout.fillWidth: true } TextArea { id: bodyTextArea - placeholderText: "Text" + placeholderText: qsTr("Text") Layout.fillWidth: true } Button { Layout.fillWidth: true - text: "Send" + text: qsTr("Send") onClicked: { var params = [] diff --git a/mea/ui/customviews/SensorView.qml b/mea/ui/customviews/SensorView.qml index 5400f58f..284fe200 100644 --- a/mea/ui/customviews/SensorView.qml +++ b/mea/ui/customviews/SensorView.qml @@ -59,7 +59,7 @@ CustomViewBase { id: zoomTabBar Layout.fillWidth: true TabButton { - text: "6 h" + text: qsTr("6 h") property int avg: ValueLogsProxyModel.AverageQuarterHour property date startTime: { var date = new Date(); @@ -70,7 +70,7 @@ CustomViewBase { } } TabButton { - text: "24 h" + text: qsTr("24 h") property int avg: ValueLogsProxyModel.AverageHourly property date startTime: { var date = new Date(); @@ -81,7 +81,7 @@ CustomViewBase { } } TabButton { - text: "7 d" + text: qsTr("7 d") property int avg: ValueLogsProxyModel.AverageDayTime property date startTime: { var date = new Date(); diff --git a/mea/ui/devicelistpages/LightsDeviceListPage.qml b/mea/ui/devicelistpages/LightsDeviceListPage.qml index 536839fd..604bf73d 100644 --- a/mea/ui/devicelistpages/LightsDeviceListPage.qml +++ b/mea/ui/devicelistpages/LightsDeviceListPage.qml @@ -7,7 +7,7 @@ import "../components" Page { property alias filterInterface: devicesProxy.filterInterface header: GuhHeader { - text: "Lights" + text: qsTr("Lights") onBackPressed: pageStack.pop() } ColumnLayout { @@ -16,11 +16,11 @@ Page { Layout.fillWidth: true Layout.margins: 10 Label { - text: "All" + text: qsTr("All") Layout.fillWidth: true } Button { - text: "off" + text: qsTr("off") onClicked: { for (var i = 0; i < devicesProxy.count; i++) { var device = devicesProxy.get(i); diff --git a/mea/ui/devicepages/ButtonDevicePage.qml b/mea/ui/devicepages/ButtonDevicePage.qml index befa1f41..c2de5725 100644 --- a/mea/ui/devicepages/ButtonDevicePage.qml +++ b/mea/ui/devicepages/ButtonDevicePage.qml @@ -26,7 +26,7 @@ DevicePageBase { Label { Layout.fillWidth: true Layout.margins: app.margins - text: "When this switch is pressed..." + text: qsTr("When this switch is pressed...") visible: actionListView.count > 0 } @@ -42,15 +42,15 @@ DevicePageBase { delegate: SwipeDelegate { width: parent.width property var ruleActions: rulesFilterModel.get(index).actions - property var ruleAction: ruleActions.count == 1 ? ruleActions.get(0) : null + property var ruleAction: ruleActions.count === 1 ? ruleActions.get(0) : null property var ruleActionType: ruleAction ? ruleActionDeviceClass.actionTypes.getActionType(ruleAction.actionTypeId) : null property var ruleActionDevice: ruleAction ? Engine.deviceManager.devices.getDevice(ruleAction.deviceId) : null property var ruleActionDeviceClass: ruleActionDevice ? Engine.deviceManager.deviceClasses.getDeviceClass(ruleActionDevice.deviceClassId) : null property var ruleActionParams: ruleAction && ruleAction ? ruleAction.ruleActionParams : null - property var ruleActionParam: ruleActionParams.count == 1 ? ruleActionParams.get(0) : null + property var ruleActionParam: ruleActionParams.count === 1 ? ruleActionParams.get(0) : null text: { if (ruleActions && ruleActions.count > 1) { - return "Multiple actions"; + return qsTr("Multiple actions"); } else if (ruleActionParam) { return qsTr("%1: Set %2 to %3").arg(ruleActionDevice.name).arg(ruleActionType.name).arg(ruleActionParam.value) } else { @@ -79,7 +79,7 @@ DevicePageBase { wrapMode: Text.WordWrap horizontalAlignment: Text.AlignHCenter anchors.centerIn: parent - text: "No actions configured for this switch. You may add some actions for this switch by using the \"Add action\" button at the bottom." + text: qsTr("No actions configured for this switch. You may add some actions for this switch by using the \"Add action\" button at the bottom.") visible: actionListView.count == 0 } } @@ -87,9 +87,9 @@ DevicePageBase { Button { Layout.fillWidth: true Layout.margins: app.margins - text: "Add an action" + text: qsTr("Add an action") onClicked: { - var page = pageStack.push(Qt.resolvedUrl("../magic/SelectActionPage.qml"), {text: "When this switch is pressed..."}); + var page = pageStack.push(Qt.resolvedUrl("../magic/SelectActionPage.qml"), {text: qsTr("When this switch is pressed...")}); page.complete.connect(function() { print("have action:", page.device, page.actionType, page.params) var rule = {}; diff --git a/mea/ui/devicepages/ConfigureThingPage.qml b/mea/ui/devicepages/ConfigureThingPage.qml index f817aea8..33668734 100644 --- a/mea/ui/devicepages/ConfigureThingPage.qml +++ b/mea/ui/devicepages/ConfigureThingPage.qml @@ -26,12 +26,12 @@ Page { x: parent.width - width IconMenuItem { iconSource: "../images/delete.svg" - text: "Delete Thing" + text: qsTr("Delete Thing") onTriggered: Engine.deviceManager.removeDevice(root.device.id) } IconMenuItem { iconSource: "../images/edit.svg" - text: "Rename Thing" + text: qsTr("Rename Thing") onTriggered: { var popup = renameDialog.createObject(root); popup.open(); @@ -51,7 +51,7 @@ Page { popup.open(); return; default: - var popup = errorDialog.createObject(root, {text: "Remove device error: " + JSON.stringify(params.deviceError) }) + var popup = errorDialog.createObject(root, {text: qsTr("Remove device error: %1").arg(JSON.stringify(params.deviceError)) }) popup.open(); } } @@ -68,7 +68,7 @@ Page { Layout.leftMargin: app.margins Layout.rightMargin: app.margins Layout.topMargin: app.margins - text: "Thing parameters".toUpperCase() + text: qsTr("Thing parameters").toUpperCase() color: app.guhAccent } diff --git a/mea/ui/images/bluetooth.svg b/mea/ui/images/bluetooth.svg new file mode 100644 index 00000000..47fe9c5c --- /dev/null +++ b/mea/ui/images/bluetooth.svg @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/mea/ui/images/guh-logo.svg b/mea/ui/images/guh-logo.svg new file mode 100644 index 00000000..44a367c4 --- /dev/null +++ b/mea/ui/images/guh-logo.svg @@ -0,0 +1,595 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mea/ui/images/network-vpn.svg b/mea/ui/images/network-vpn.svg new file mode 100644 index 00000000..ebcf2954 --- /dev/null +++ b/mea/ui/images/network-vpn.svg @@ -0,0 +1,23 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/mea/ui/images/nm-signal-00-secure.svg b/mea/ui/images/nm-signal-00-secure.svg new file mode 100644 index 00000000..a18e4e76 --- /dev/null +++ b/mea/ui/images/nm-signal-00-secure.svg @@ -0,0 +1,18 @@ + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/mea/ui/images/nm-signal-00.svg b/mea/ui/images/nm-signal-00.svg new file mode 100644 index 00000000..eed7a79f --- /dev/null +++ b/mea/ui/images/nm-signal-00.svg @@ -0,0 +1,17 @@ + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/mea/ui/images/nm-signal-100-secure.svg b/mea/ui/images/nm-signal-100-secure.svg new file mode 100644 index 00000000..f32b1f15 --- /dev/null +++ b/mea/ui/images/nm-signal-100-secure.svg @@ -0,0 +1,18 @@ + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/mea/ui/images/nm-signal-100.svg b/mea/ui/images/nm-signal-100.svg new file mode 100644 index 00000000..d0b2cc8d --- /dev/null +++ b/mea/ui/images/nm-signal-100.svg @@ -0,0 +1,17 @@ + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/mea/ui/images/nm-signal-25-secure.svg b/mea/ui/images/nm-signal-25-secure.svg new file mode 100644 index 00000000..e22a807f --- /dev/null +++ b/mea/ui/images/nm-signal-25-secure.svg @@ -0,0 +1,19 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/mea/ui/images/nm-signal-25.svg b/mea/ui/images/nm-signal-25.svg new file mode 100644 index 00000000..6d5a94eb --- /dev/null +++ b/mea/ui/images/nm-signal-25.svg @@ -0,0 +1,18 @@ + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/mea/ui/images/nm-signal-50-secure.svg b/mea/ui/images/nm-signal-50-secure.svg new file mode 100644 index 00000000..46cef540 --- /dev/null +++ b/mea/ui/images/nm-signal-50-secure.svg @@ -0,0 +1,19 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/mea/ui/images/nm-signal-50.svg b/mea/ui/images/nm-signal-50.svg new file mode 100644 index 00000000..7e066ddf --- /dev/null +++ b/mea/ui/images/nm-signal-50.svg @@ -0,0 +1,18 @@ + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/mea/ui/images/nm-signal-75-secure.svg b/mea/ui/images/nm-signal-75-secure.svg new file mode 100644 index 00000000..080a4a1d --- /dev/null +++ b/mea/ui/images/nm-signal-75-secure.svg @@ -0,0 +1,19 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/mea/ui/images/nm-signal-75.svg b/mea/ui/images/nm-signal-75.svg new file mode 100644 index 00000000..cca56083 --- /dev/null +++ b/mea/ui/images/nm-signal-75.svg @@ -0,0 +1,18 @@ + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/mea/ui/images/refresh.svg b/mea/ui/images/refresh.svg new file mode 100644 index 00000000..4b5d3526 --- /dev/null +++ b/mea/ui/images/refresh.svg @@ -0,0 +1,18 @@ + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/mea/ui/magic/ComposeEventDescriptorPage.qml b/mea/ui/magic/ComposeEventDescriptorPage.qml index 079a7bbd..35baceea 100644 --- a/mea/ui/magic/ComposeEventDescriptorPage.qml +++ b/mea/ui/magic/ComposeEventDescriptorPage.qml @@ -9,7 +9,7 @@ Page { property var device: null header: GuhHeader { - text: "Select event" + text: qsTr("Select event") } ColumnLayout { @@ -22,11 +22,11 @@ Page { RowLayout { Layout.fillWidth: true RadioButton { - text: "A specific thing" + text: qsTr("A specific thing") checked: true } RadioButton { - text: "A group of things" + text: qsTr("A group of things") } } diff --git a/mea/ui/magic/EditRulePage.qml b/mea/ui/magic/EditRulePage.qml index 7825fe10..4522bfd0 100644 --- a/mea/ui/magic/EditRulePage.qml +++ b/mea/ui/magic/EditRulePage.qml @@ -91,7 +91,7 @@ Page { } header: GuhHeader { - text: "New rule" + text: qsTr("New rule") onBackPressed: pageStack.pop() HeaderButton { imageSource: "../images/tick.svg" @@ -115,7 +115,7 @@ Page { Layout.margins: app.margins Label { Layout.fillWidth: true - text: "Rule name" + text: qsTr("Rule name") } TextField { Layout.fillWidth: true @@ -146,7 +146,7 @@ Page { Layout.fillWidth: true Layout.margins: app.margins font.pixelSize: app.mediumFont - text: "Events triggering this rule" + text: qsTr("Events triggering this rule") visible: !root.hasExitActions } @@ -224,7 +224,7 @@ Page { Button { Layout.fillWidth: true Layout.margins: app.margins - text: eventsRepeater.count == 0 ? "Add an event..." : "Add another event..." + text: eventsRepeater.count == 0 ? qsTr("Add an event...") : qsTr("Add another event...") onClicked: root.addEventDescriptor(); visible: !root.hasExitActions } @@ -232,7 +232,7 @@ Page { ThinDivider {} Label { - text: "Conditions to be met" + text: qsTr("Conditions to be met") font.pixelSize: app.mediumFont Layout.fillWidth: true Layout.margins: app.margins @@ -247,7 +247,7 @@ Page { Button { Layout.fillWidth: true Layout.margins: app.margins - text: "Add a condition" + text: qsTr("Add a condition") visible: root.rule.stateEvaluator === null onClicked: { root.rule.createStateEvaluator(); @@ -258,7 +258,7 @@ Page { ThinDivider { visible: root.actionsVisible } Label { - text: root.isStateBased ? "Active state enter actions" : "Actions to execute" + text: root.isStateBased ? qsTr("Active state enter actions") : qsTr("Actions to execute") font.pixelSize: app.mediumFont Layout.fillWidth: true Layout.margins: app.margins @@ -290,7 +290,7 @@ Page { model: actionDelegate.ruleAction.ruleActionParams Label { text: actionDelegate.actionType.paramTypes.getParamType(model.paramTypeId).displayName + " -> " + - (model.eventParamTypeId.length > 0 ? "value from event" : model.value) + (model.eventParamTypeId.length > 0 ? qsTr("value from event") : model.value) font.pixelSize: app.smallFont } } @@ -314,7 +314,7 @@ Page { Button { Layout.fillWidth: true Layout.margins: app.margins - text: actionsRepeater.count == 0 ? "Add an action..." : "Add another action..." + text: actionsRepeater.count == 0 ? qsTr("Add an action...") : qsTr("Add another action...") onClicked: root.addAction(); visible: root.actionsVisible } @@ -322,7 +322,7 @@ Page { ThinDivider { visible: root.exitActionsVisible } Label { - text: "Active state exit actions" + text: qsTr("Active state exit actions") font.pixelSize: app.mediumFont Layout.fillWidth: true Layout.margins: app.margins @@ -378,7 +378,7 @@ Page { Button { Layout.fillWidth: true Layout.margins: app.margins - text: actionsRepeater.count == 0 ? "Add an action..." : "Add another action..." + text: actionsRepeater.count == 0 ? qsTr("Add an action...") : qsTr("Add another action...") onClicked: root.addExitAction(); visible: root.exitActionsVisible } diff --git a/mea/ui/magic/EditStateEvaluatorPage.qml b/mea/ui/magic/EditStateEvaluatorPage.qml index 52016453..26b0e389 100644 --- a/mea/ui/magic/EditStateEvaluatorPage.qml +++ b/mea/ui/magic/EditStateEvaluatorPage.qml @@ -7,7 +7,7 @@ import "../components" Page { id: root header: GuhHeader { - text: "Conditions" + text: qsTr("Conditions") onBackPressed: pageStack.pop() } diff --git a/mea/ui/magic/NewThingMagicPage.qml b/mea/ui/magic/NewThingMagicPage.qml index 6428c440..04849fdd 100644 --- a/mea/ui/magic/NewThingMagicPage.qml +++ b/mea/ui/magic/NewThingMagicPage.qml @@ -16,23 +16,23 @@ Page { ListModel { id: eventModel - ListElement { interfaceName: "temperaturesensor"; text: "When it's freezing..."; identifier: "freeze"} - ListElement { interfaceName: "battery"; text: "When the device runs out of battery..."; identifier: "lowBattery"} - ListElement { interfaceName: "weather"; text: "When it starts raining..."; identifier: "rain" } - ListElement { interfaceName: "weather"; text: "When it's freezing..."; identifier: "freeze"} + ListElement { interfaceName: "temperaturesensor"; text: qsTr("When it's freezing..."); identifier: "freeze"} + ListElement { interfaceName: "battery"; text: qsTr("When the device runs out of battery..."); identifier: "lowBattery"} + ListElement { interfaceName: "weather"; text: qsTr("When it starts raining..."); identifier: "rain" } + ListElement { interfaceName: "weather"; text: qsTr("When it's freezing..."); identifier: "freeze"} } ListModel { id: actionModel - ListElement { interfaceName: "light"; text: "Switch light when..."; identifier: "switchLight"} - ListElement { interfaceName: "dimmablelight"; text: "Dim light when..."; identifier: "dimLight"} - ListElement { interfaceName: "colorlight"; text: "Set light color when..."; identifier: "colorLight" } - ListElement { interfaceName: "mediacontroller"; text: "Pause playback when..."; identifier: "pausePlayback" } - ListElement { interfaceName: "mediacontroller"; text: "Resume playback when..."; identifier: "resumePlayback" } - ListElement { interfaceName: "extendedvolumecontroller"; text: "Set volume..."; identifier: "setVolume" } - ListElement { interfaceName: "extendedvolumecontroller"; text: "Mute when..."; identifier: "mute" } - ListElement { interfaceName: "extendedvolumecontroller"; text: "Unmute when..."; identifier: "unmute" } - ListElement { interfaceName: "notifications"; text: "Notify me when..."; identifier: "notify" } + ListElement { interfaceName: "light"; text: qsTr("Switch light when..."); identifier: "switchLight"} + ListElement { interfaceName: "dimmablelight"; text: qsTr("Dim light when..."); identifier: "dimLight"} + ListElement { interfaceName: "colorlight"; text: qsTr("Set light color when..."); identifier: "colorLight" } + ListElement { interfaceName: "mediacontroller"; text: qsTr("Pause playback when..."); identifier: "pausePlayback" } + ListElement { interfaceName: "mediacontroller"; text: qsTr("Resume playback when..."); identifier: "resumePlayback" } + ListElement { interfaceName: "extendedvolumecontroller"; text: qsTr("Set volume..."); identifier: "setVolume" } + ListElement { interfaceName: "extendedvolumecontroller"; text: qsTr("Mute when..."); identifier: "mute" } + ListElement { interfaceName: "extendedvolumecontroller"; text: qsTr("Unmute when..."); identifier: "unmute" } + ListElement { interfaceName: "notifications"; text: qsTr("Notify me when..."); identifier: "notify" } } function entrySelected(identifier) { diff --git a/mea/ui/magic/SelectActionPage.qml b/mea/ui/magic/SelectActionPage.qml index 3e200f7e..8a082028 100644 --- a/mea/ui/magic/SelectActionPage.qml +++ b/mea/ui/magic/SelectActionPage.qml @@ -22,11 +22,11 @@ Page { ListModel { id: actionModel - ListElement { interfaceName: "light"; text: "Switch lights..."; identifier: "switchLights" } - ListElement { interfaceName: "mediacontroller"; text: "Control media playback..."; identifier: "controlMedia" } - ListElement { interfaceName: "extendedvolumecontroller"; text: "Mute media playback..."; identifier: "muteMedia" } - ListElement { interfaceName: "notifications"; text: "Notify me..."; identifier: "notify" } - ListElement { interfaceName: ""; text: "Manually configure an action..."; identifier: "manualAction" } + ListElement { interfaceName: "light"; text: qsTr("Switch lights..."); identifier: "switchLights" } + ListElement { interfaceName: "mediacontroller"; text: qsTr("Control media playback..."); identifier: "controlMedia" } + ListElement { interfaceName: "extendedvolumecontroller"; text: qsTr("Mute media playback..."); identifier: "muteMedia" } + ListElement { interfaceName: "notifications"; text: qsTr("Notify me..."); identifier: "notify" } + ListElement { interfaceName: ""; text: qsTr("Manually configure an action..."); identifier: "manualAction" } } DevicesProxy { @@ -91,7 +91,7 @@ Page { id: selectDeviceComponent Page { header: GuhHeader { - text: "Select device" + text: qsTr("Select device") onBackPressed: pageStack.pop() } @@ -128,7 +128,7 @@ Page { readonly property var deviceClass: Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) header: GuhHeader { - text: "Select action" + text: qsTr("Select action") onBackPressed: pageStack.pop() } @@ -150,7 +150,7 @@ Page { onClicked: { var actionType = page.deviceClass.actionTypes.get(index) - if (page.deviceClass.actionTypes.get(index).paramTypes.count == 0) { + if (page.deviceClass.actionTypes.get(index).paramTypes.count === 0) { // We're all set. var action = {} action["deviceId"] = page.device.id @@ -175,7 +175,7 @@ Page { property var device property var actionType header: GuhHeader { - text: "params" + text: qsTr("params") onBackPressed: pageStack.pop() } @@ -195,7 +195,7 @@ Page { Layout.fillHeight: true } Button { - text: "OK" + text: qsTr("OK") Layout.fillWidth: true Layout.margins: app.margins onClicked: { @@ -223,7 +223,7 @@ Page { id: switchLightsCompoent Page { header: GuhHeader { - text: "Switch lights" + text: qsTr("Switch lights") onBackPressed: pageStack.pop() } @@ -233,7 +233,7 @@ Page { SwitchDelegate { id: switchDelegate Layout.fillWidth: true - text: "Set selected lights power to" + text: qsTr("Set selected lights power to") position: 0 } ThinDivider {} @@ -266,7 +266,7 @@ Page { Button { Layout.fillWidth: true Layout.margins: app.margins - text: "OK" + text: qsTr("OK") onClicked: { for (var i = 0; i < lightsRepeater.count; i++) { if (lightsRepeater.itemAt(i).checkState === Qt.Unchecked) { @@ -302,7 +302,7 @@ Page { id: notificationActionComponent Page { header: GuhHeader { - text: "Send notification" + text: qsTr("Send notification") onBackPressed: pageStack.pop() } @@ -311,7 +311,7 @@ Page { spacing: app.margins Label { Layout.fillWidth: true - text: "Notification text" + text: qsTr("Notification text") Layout.topMargin: app.margins Layout.leftMargin: app.margins Layout.rightMargin: app.margins @@ -351,7 +351,7 @@ Page { Button { Layout.fillWidth: true Layout.margins: app.margins - text: "OK" + text: qsTr("OK") onClicked: { var action = {} action["interface"] = "notifications"; diff --git a/mea/ui/magic/SelectEventDescriptorPage.qml b/mea/ui/magic/SelectEventDescriptorPage.qml index 466cd552..4e731617 100644 --- a/mea/ui/magic/SelectEventDescriptorPage.qml +++ b/mea/ui/magic/SelectEventDescriptorPage.qml @@ -35,9 +35,9 @@ Page { ListModel { id: eventTemplateModel - ListElement { interfaceName: "temperaturesensor"; text: "When it's freezing..."; event: "freeze"} - ListElement { interfaceName: "battery"; text: "When the device runs out of battery..."; event: "lowBattery"} - ListElement { interfaceName: "weather"; text: "When it starts raining..."; event: "rain" } + ListElement { interfaceName: "temperaturesensor"; text: qsTr("When it's freezing..."); event: "freeze"} + ListElement { interfaceName: "battery"; text: qsTr("When the device runs out of battery..."); event: "lowBattery"} + ListElement { interfaceName: "weather"; text: qsTr("When it starts raining..."); event: "rain" } } function buildInterface() { @@ -81,7 +81,7 @@ Page { default: console.warn("FIXME: Unhandled interface event"); } - } else if (root.eventDescriptor.interfaceName != "") { + } else if (root.eventDescriptor.interfaceName !== "") { root.eventDescriptor.interfaceEvent = model.name; if (listView.model.get(index).paramTypes.count > 0) { var paramsPage = pageStack.push(Qt.resolvedUrl("SelectEventDescriptorParamsPage.qml"), {eventDescriptor: root.eventDescriptor}) diff --git a/mea/ui/magic/SelectEventDescriptorParamsPage.qml b/mea/ui/magic/SelectEventDescriptorParamsPage.qml index 00169069..7dab50ac 100644 --- a/mea/ui/magic/SelectEventDescriptorParamsPage.qml +++ b/mea/ui/magic/SelectEventDescriptorParamsPage.qml @@ -36,7 +36,7 @@ Page { property alias operatorType: paramDescriptorDelegate.operatorType CheckBox { id: paramCheckBox - text: "Only consider event if" + text: qsTr("Only consider event if") Layout.fillWidth: true Layout.leftMargin: app.margins Layout.rightMargin: app.margins diff --git a/mea/ui/magic/SelectRuleActionPage.qml b/mea/ui/magic/SelectRuleActionPage.qml index f7a33a0d..78e179de 100644 --- a/mea/ui/magic/SelectRuleActionPage.qml +++ b/mea/ui/magic/SelectRuleActionPage.qml @@ -87,7 +87,7 @@ Page { default: console.warn("FIXME: Unhandled interface action"); } - } else if (root.ruleAction.interfaceName != "") { + } else if (root.ruleAction.interfaceName !== "") { root.ruleAction.interfaceAction = model.name; if (listView.model.get(index).paramTypes.count > 0) { var paramsPage = pageStack.push(Qt.resolvedUrl("SelectRuleActionParamsPage.qml"), {ruleAction: root.ruleAction, rule: root.rule}) diff --git a/mea/ui/magic/SelectRuleActionParamsPage.qml b/mea/ui/magic/SelectRuleActionParamsPage.qml index 570f8afa..4367a7e1 100644 --- a/mea/ui/magic/SelectRuleActionParamsPage.qml +++ b/mea/ui/magic/SelectRuleActionParamsPage.qml @@ -107,7 +107,7 @@ Page { Layout.fillHeight: true } Button { - text: "OK" + text: qsTr("OK") Layout.fillWidth: true Layout.margins: app.margins onClicked: { diff --git a/mea/ui/magic/SelectStateDescriptorParamsPage.qml b/mea/ui/magic/SelectStateDescriptorParamsPage.qml index 12d97652..57430a11 100644 --- a/mea/ui/magic/SelectStateDescriptorParamsPage.qml +++ b/mea/ui/magic/SelectStateDescriptorParamsPage.qml @@ -17,7 +17,7 @@ Page { signal completed(); header: GuhHeader { - text: "params" + text: qsTr("params") onBackPressed: root.backPressed(); } @@ -30,7 +30,7 @@ Page { value: paramType.defaultValue } Button { - text: "OK" + text: qsTr("OK") Layout.fillWidth: true Layout.margins: app.margins onClicked: { diff --git a/mea/ui/magic/SelectThingPage.qml b/mea/ui/magic/SelectThingPage.qml index b0fdfcd0..f5e0a935 100644 --- a/mea/ui/magic/SelectThingPage.qml +++ b/mea/ui/magic/SelectThingPage.qml @@ -12,7 +12,7 @@ Page { signal interfaceSelected(string interfaceName); header: GuhHeader { - text: "Select a thing" + text: qsTr("Select a thing") onBackPressed: root.backPressed() } ColumnLayout { @@ -22,12 +22,12 @@ Page { Layout.fillWidth: true RadioButton { id: thingButton - text: "A specific thing" + text: qsTr("A specific thing") checked: true } RadioButton { id: interfacesButton - text: "A group of things" + text: qsTr("A group of things") } } diff --git a/mea/ui/magic/StateEvaluatorDelegate.qml b/mea/ui/magic/StateEvaluatorDelegate.qml index 7559de97..e7904382 100644 --- a/mea/ui/magic/StateEvaluatorDelegate.qml +++ b/mea/ui/magic/StateEvaluatorDelegate.qml @@ -29,7 +29,7 @@ SwipeDelegate { ComboBox { Layout.fillWidth: true - model: ["and all of those", "or any of those"] + model: [qsTr("and all of those"), qsTr("or any of those")] currentIndex: root.stateEvaluator && root.stateEvaluator.stateOperator === StateEvaluator.StateOperatorAnd ? 0 : 1 visible: root.stateEvaluator && root.stateEvaluator.childEvaluators.count > 0 onActivated: { @@ -51,7 +51,7 @@ SwipeDelegate { Button { Layout.fillWidth: true - text: "Add a condition" + text: qsTr("Add a condition") onClicked: { root.stateEvaluator.addChildEvaluator() // root.editStateEvaluator() diff --git a/mea/ui/main.qml b/mea/ui/main.qml index 5b311f46..2972c499 100644 --- a/mea/ui/main.qml +++ b/mea/ui/main.qml @@ -26,6 +26,7 @@ ApplicationWindow { property string lastConnectedHost: "" property int viewMode: ApplicationWindow.Maximized property bool returnToHome: false + property bool darkTheme: false property string graphStyle: "bars" property string style: "light" } @@ -133,27 +134,27 @@ ApplicationWindow { function interfaceToString(name) { switch(name) { case "light": - return "Lighting" + return qsTr("Lighting") case "weather": - return "Weather" + return qsTr("Weather") case "sensor": - return "Sensors" + return qsTr("Sensors") case "media": - return "Media" + return qsTr("Media") case "button": - return "Switches" + return qsTr("Switches") case "gateway": - return "Gateways" + return qsTr("Gateways") case "notifications": - return "Notifications" + return qsTr("Notifications") case "temperaturesensor": - return "Temperature"; + return qsTr("Temperature"); case "humiditysensor": - return "Humidity"; + return qsTr("Humidity"); case "inputtrigger": - return "Incoming Events"; + return qsTr("Incoming Events"); case "outputtrigger": - return "Events"; + return qsTr("Events"); } } @@ -233,7 +234,7 @@ ApplicationWindow { anchors { left: parent.left; right: parent.right } spacing: app.margins Label { - text: "Connection error" + text: qsTr("Connection error") Layout.fillWidth: true font.pixelSize: app.largeFont } @@ -244,7 +245,7 @@ ApplicationWindow { } Button { Layout.fillWidth: true - text: "OK" + text: qsTr("OK") onClicked: { Engine.connection.disconnect(); popup.close() diff --git a/mea/ui/paramdelegates-ng/ParamDelegate.qml b/mea/ui/paramdelegates-ng/ParamDelegate.qml index 4c6f8ca8..54116a44 100644 --- a/mea/ui/paramdelegates-ng/ParamDelegate.qml +++ b/mea/ui/paramdelegates-ng/ParamDelegate.qml @@ -26,7 +26,7 @@ ItemDelegate { } Loader { id: loader - Layout.fillWidth: sourceComponent == textFieldComponent + Layout.fillWidth: sourceComponent === textFieldComponent sourceComponent: { if (!root.writable) { return stringComponent; @@ -60,7 +60,7 @@ ItemDelegate { switch (root.paramType.type.toLowerCase()) { case "int": case "double": - if (root.paramType.minValue != undefined && root.paramType.maxValue != undefined) { + if (root.paramType.minValue !== undefined && root.paramType.maxValue !== undefined) { return sliderComponent } break; diff --git a/mea/ui/paramdescriptordelegates/ParamDescriptorDelegateBase.qml b/mea/ui/paramdescriptordelegates/ParamDescriptorDelegateBase.qml index 80e68275..904fa029 100644 --- a/mea/ui/paramdescriptordelegates/ParamDescriptorDelegateBase.qml +++ b/mea/ui/paramdescriptordelegates/ParamDescriptorDelegateBase.qml @@ -24,30 +24,30 @@ ItemDelegate { case "bool": case "string": case "qstring": - return ["is", "is not"]; + return [qsTr("is"), qsTr("is not")]; case "int": case "double": - return ["is", "is not", "is greater", "is smaller", "is greater or equal", "is smaller or equal"] + return [qsTr("is"), qsTr("is not"), qsTr("is greater"), qsTr("is smaller"), qsTr("is greater or equal"), qsTr("is smaller or equal")] } } onCurrentTextChanged: { switch (currentText) { - case "is": + case qsTr("is"): root.operatorType = ParamDescriptor.ValueOperatorEquals; break; - case "is not": + case qsTr("is not"): root.operatorType = ParamDescriptor.ValueOperatorNotEquals; break; - case "is greater": + case qsTr("is greater"): root.operatorType = ParamDescriptor.ValueOperatorGreater; break; - case "is smaller": + case qsTr("is smaller"): root.operatorType = ParamDescriptor.ValueOperatorLess; break; - case "is greater or equal": + case qsTr("is greater or equal"): root.operatorType = ParamDescriptor.ValueOperatorGreaterOrEqual; break; - case "is smaller or equal": + case qsTr("is smaller or equal"): root.operatorType = ParamDescriptor.ValueOperatorLessOrEqual; break; } diff --git a/mea/ui/system/LogViewerPage.qml b/mea/ui/system/LogViewerPage.qml index 31208fe4..dd3febc3 100644 --- a/mea/ui/system/LogViewerPage.qml +++ b/mea/ui/system/LogViewerPage.qml @@ -8,7 +8,7 @@ import "../components" Page { id: root header: GuhHeader { - text: "Log viewer" + text: qsTr("Log viewer") onBackPressed: pageStack.pop() HeaderButton { @@ -66,24 +66,24 @@ Page { anchors.verticalCenter: parent.verticalCenter Label { width: listView.column0Width - text: "Time" + text: qsTr("Time") } Label { - text: "Type" + text: qsTr("Type") width: listView.column1Width } Label { width: listView.column2Width - text: "Thing" + text: qsTr("Thing") } Label { width: listView.column3Width - text: "Object" + text: qsTr("Object") } Label { width: listView.column4Width - text: "Value" + text: qsTr("Value") } } ThinDivider { @@ -134,7 +134,7 @@ Page { Label { width: listView.column2Width - text: model.source === LogEntry.LoggingSourceSystem ? "Nymea Server" : delegate.device.name + text: model.source === LogEntry.LoggingSourceSystem ? qsTr("Nymea Server") : delegate.device.name elide: Text.ElideRight } Label { @@ -144,7 +144,7 @@ Page { case LogEntry.LoggingSourceStates: return delegate.deviceClass.stateTypes.getStateType(model.typeId).displayName; case LogEntry.LoggingSourceSystem: - return model.loggingEventType === LogEntry.LoggingEventTypeActiveChange ? "Active changed" : "FIXME" + return model.loggingEventType === LogEntry.LoggingEventTypeActiveChange ? qsTr("Active changed") : "FIXME" case LogEntry.LoggingSourceActions: return delegate.deviceClass.actionTypes.getActionType(model.typeId).displayName; case LogEntry.LoggingSourceEvents: diff --git a/mea/ui/system/PluginParamsPage.qml b/mea/ui/system/PluginParamsPage.qml index 9a4d833f..cfee18c5 100644 --- a/mea/ui/system/PluginParamsPage.qml +++ b/mea/ui/system/PluginParamsPage.qml @@ -26,11 +26,11 @@ Page { Connections { target: Engine.deviceManager onSavePluginConfigReply: { - if (params.params.deviceError == "DeviceErrorNoError") { + if (params.params.deviceError === "DeviceErrorNoError") { pageStack.pop(); } else { console.warn("Error saving plugin params:", JSON.stringify(params)) - var dialog = errorDialog.createObject(root, {title: "Error", text: "Error saving params: " + JSON.stringify(params.params.deviceError)}); + var dialog = errorDialog.createObject(root, {title: "Error", text: qsTr("Error saving params: ") + JSON.stringify(params.params.deviceError)}); dialog.open(); } } diff --git a/mea/ui/system/PluginsPage.qml b/mea/ui/system/PluginsPage.qml index da6c046d..aa7c7f84 100644 --- a/mea/ui/system/PluginsPage.qml +++ b/mea/ui/system/PluginsPage.qml @@ -8,7 +8,7 @@ import Mea 1.0 Page { id: root header: GuhHeader { - text: "Plugins" + text: qsTr("Plugins") backButtonVisible: true onBackPressed: pageStack.pop() } diff --git a/mea/wifisetup/bluetoothdevice.cpp b/mea/wifisetup/bluetoothdevice.cpp new file mode 100644 index 00000000..4ac7db94 --- /dev/null +++ b/mea/wifisetup/bluetoothdevice.cpp @@ -0,0 +1,138 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Simon Stuerz * + * * + * This file is part of mea * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "bluetoothdevice.h" + +BluetoothDevice::BluetoothDevice(const QBluetoothDeviceInfo &deviceInfo, QObject *parent) : + QObject(parent), + m_deviceInfo(deviceInfo), + m_connected(false) +{ + m_controller = new QLowEnergyController(deviceInfo.address(), this); + m_controller->setRemoteAddressType(QLowEnergyController::PublicAddress); + + connect(m_controller, &QLowEnergyController::connected, this, &BluetoothDevice::onConnected); + connect(m_controller, &QLowEnergyController::disconnected, this, &BluetoothDevice::onDisconnected); + connect(m_controller, &QLowEnergyController::stateChanged, this, &BluetoothDevice::onDeviceStateChanged); + connect(m_controller, SIGNAL(error(QLowEnergyController::Error)), this, SLOT(onDeviceError(QLowEnergyController::Error))); + + connect(m_controller, SIGNAL(discoveryFinished()), this, SIGNAL(serviceDiscoveryFinished())); +} + +QString BluetoothDevice::name() const +{ + return m_deviceInfo.name(); +} + +QBluetoothAddress BluetoothDevice::address() const +{ + return m_deviceInfo.address(); +} + +bool BluetoothDevice::connected() const +{ + return m_connected; +} + +QString BluetoothDevice::statusText() const +{ + return m_statusText; +} + +void BluetoothDevice::connectDevice() +{ + m_controller->connectToDevice(); +} + +void BluetoothDevice::disconnectDevice() +{ + m_controller->disconnectFromDevice(); +} + +void BluetoothDevice::setConnected(const bool &connected) +{ + m_connected = connected; + emit connectedChanged(); +} + +void BluetoothDevice::setStatusText(const QString &statusText) +{ + m_statusText = statusText; + emit statusTextChanged(); +} + +QLowEnergyController *BluetoothDevice::controller() +{ + return m_controller; +} + +void BluetoothDevice::onConnected() +{ + qDebug() << "BluetoothDevice: Connected to" << name() << address().toString(); + m_controller->discoverServices(); +} + +void BluetoothDevice::onDisconnected() +{ + qWarning() << "BluetoothDevice: Disconnected from" << name() << address().toString(); + setConnected(false); + setStatusText("Disconnected from " + name()); +} + +void BluetoothDevice::onDeviceError(const QLowEnergyController::Error &error) +{ + qWarning() << "BluetoothDevice: Error" << name() << address().toString() << ": " << error << m_controller->errorString(); + setConnected(false); +} + +void BluetoothDevice::onDeviceStateChanged(const QLowEnergyController::ControllerState &state) +{ + switch (state) { + case QLowEnergyController::ConnectingState: + qDebug() << "BluetoothDevice: Connecting..."; + setStatusText(QString(tr("Connecting to %1...").arg(name()))); + break; + case QLowEnergyController::ConnectedState: + qDebug() << "BluetoothDevice: Connected!"; + setStatusText(QString(tr("Connected to %1").arg(name()))); + break; + case QLowEnergyController::ClosingState: + qDebug() << "BluetoothDevice: Connection: Closing..."; + setStatusText(QString(tr("Disconnecting from %1...").arg(name()))); + break; + case QLowEnergyController::DiscoveringState: + qDebug() << "BluetoothDevice: Discovering..."; + setStatusText(QString(tr("Discovering services of %1...").arg(name()))); + break; + case QLowEnergyController::DiscoveredState: + qDebug() << "BluetoothDevice: Discovered!"; + setStatusText(QString(tr("%1 connected and discovered.").arg(name()))); + setConnected(true); + break; + case QLowEnergyController::UnconnectedState: + qDebug() << "BluetoothDevice: Not connected."; + setStatusText(QString(tr("%1 disconnected.").arg(name()))); + break; + default: + break; + } +} diff --git a/mea/wifisetup/bluetoothdevice.h b/mea/wifisetup/bluetoothdevice.h new file mode 100644 index 00000000..97392cf3 --- /dev/null +++ b/mea/wifisetup/bluetoothdevice.h @@ -0,0 +1,77 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Simon Stuerz * + * * + * This file is part of mea * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef BLUETOOTHDEVICE_H +#define BLUETOOTHDEVICE_H + +#include +#include +#include +#include +#include +#include + +class BluetoothDevice : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool connected READ connected NOTIFY connectedChanged) + Q_PROPERTY(QString statusText READ statusText NOTIFY statusTextChanged) + +public: + explicit BluetoothDevice(const QBluetoothDeviceInfo &deviceInfo, QObject *parent = 0); + + QString name() const; + QBluetoothAddress address() const; + bool connected() const; + QString statusText() const; + + Q_INVOKABLE void connectDevice(); + Q_INVOKABLE void disconnectDevice(); + +private: + QBluetoothDeviceInfo m_deviceInfo; + QLowEnergyController *m_controller; + bool m_connected; + QString m_statusText; + + void setConnected(const bool &connected); + +protected: + QLowEnergyController *controller(); + void setStatusText(const QString &statusText); + +signals: + void connectedChanged(); + void serviceDiscoveryFinished(); + + void statusTextChanged(); + +private slots: + void onConnected(); + void onDisconnected(); + + void onDeviceError(const QLowEnergyController::Error &error); + void onDeviceStateChanged(const QLowEnergyController::ControllerState &state); + +}; + +#endif // BLUETOOTHDEVICE_H diff --git a/mea/wifisetup/bluetoothdeviceinfo.cpp b/mea/wifisetup/bluetoothdeviceinfo.cpp new file mode 100644 index 00000000..80ca0caf --- /dev/null +++ b/mea/wifisetup/bluetoothdeviceinfo.cpp @@ -0,0 +1,58 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Simon Stuerz * + * * + * This file is part of mea * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "bluetoothdeviceinfo.h" + +BluetoothDeviceInfo::BluetoothDeviceInfo() +{ +} + +BluetoothDeviceInfo::BluetoothDeviceInfo(const QBluetoothDeviceInfo &deviceInfo) +{ + m_deviceInfo = deviceInfo; +} + +QString BluetoothDeviceInfo::address() const +{ + return m_deviceInfo.address().toString(); +} + +QString BluetoothDeviceInfo::name() const +{ + return m_deviceInfo.name(); +} + +bool BluetoothDeviceInfo::isLowEnergy() const +{ + return m_deviceInfo.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration; +} + +QBluetoothDeviceInfo BluetoothDeviceInfo::getBluetoothDeviceInfo() const +{ + return m_deviceInfo; +} + +void BluetoothDeviceInfo::setBluetoothDeviceInfo(const QBluetoothDeviceInfo &deviceInfo) +{ + m_deviceInfo = QBluetoothDeviceInfo(deviceInfo); + emit deviceChanged(); +} diff --git a/mea/wifisetup/bluetoothdeviceinfo.h b/mea/wifisetup/bluetoothdeviceinfo.h new file mode 100644 index 00000000..0d39dc8c --- /dev/null +++ b/mea/wifisetup/bluetoothdeviceinfo.h @@ -0,0 +1,56 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Simon Stuerz * + * * + * This file is part of mea * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef BLUETOOTHDEVICEINFO_H +#define BLUETOOTHDEVICEINFO_H + +#include +#include +#include +#include + +class BluetoothDeviceInfo : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString name READ name NOTIFY deviceChanged) + Q_PROPERTY(QString address READ address NOTIFY deviceChanged) + +public: + BluetoothDeviceInfo(); + BluetoothDeviceInfo(const QBluetoothDeviceInfo &deviceInfo); + + QString address() const; + QString name() const; + bool isLowEnergy() const; + + QBluetoothDeviceInfo getBluetoothDeviceInfo() const; + void setBluetoothDeviceInfo(const QBluetoothDeviceInfo &deviceInfo); + +signals: + void deviceChanged(); + +private: + QBluetoothDeviceInfo m_deviceInfo; + +}; + +#endif // BLUETOOTHDEVICEINFO_H diff --git a/mea/wifisetup/bluetoothdeviceinfos.cpp b/mea/wifisetup/bluetoothdeviceinfos.cpp new file mode 100644 index 00000000..e749b28d --- /dev/null +++ b/mea/wifisetup/bluetoothdeviceinfos.cpp @@ -0,0 +1,93 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Simon Stuerz * + * * + * This file is part of mea * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "bluetoothdeviceinfos.h" + +BluetoothDeviceInfos::BluetoothDeviceInfos(QObject *parent) : QAbstractListModel(parent) +{ + +} + +QList BluetoothDeviceInfos::deviceInfos() +{ + return m_deviceInfos; +} + +int BluetoothDeviceInfos::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return m_deviceInfos.count(); +} + +QVariant BluetoothDeviceInfos::data(const QModelIndex &index, int role) const +{ + if (index.row() < 0 || index.row() >= m_deviceInfos.count()) + return QVariant(); + + BluetoothDeviceInfo *deviceInfo = m_deviceInfos.at(index.row()); + if (role == BluetoothDeviceInfoRoleName) { + return deviceInfo->name(); + } else if (role == BluetoothDeviceInfoRoleAddress) { + return deviceInfo->address(); + } else if (role == BluetoothDeviceInfoRoleLe) { + return deviceInfo->isLowEnergy(); + } + + return QVariant(); +} + +int BluetoothDeviceInfos::count() const +{ + return m_deviceInfos.count(); +} + +BluetoothDeviceInfo *BluetoothDeviceInfos::get(int index) const +{ + if (index >= m_deviceInfos.count() || index < 0) + return Q_NULLPTR; + + return m_deviceInfos.at(index); +} + +void BluetoothDeviceInfos::addBluetoothDeviceInfo(BluetoothDeviceInfo *deviceInfo) +{ + beginInsertRows(QModelIndex(), m_deviceInfos.count(), m_deviceInfos.count()); + m_deviceInfos.append(deviceInfo); + endInsertRows(); +} + +void BluetoothDeviceInfos::clearModel() +{ + beginResetModel(); + qDeleteAll(m_deviceInfos); + m_deviceInfos.clear(); + endResetModel(); +} + +QHash BluetoothDeviceInfos::roleNames() const +{ + QHash roles; + roles[BluetoothDeviceInfoRoleName] = "name"; + roles[BluetoothDeviceInfoRoleAddress] = "address"; + roles[BluetoothDeviceInfoRoleLe] = "lowEnergy"; + return roles; +} diff --git a/mea/wifisetup/bluetoothdeviceinfos.h b/mea/wifisetup/bluetoothdeviceinfos.h new file mode 100644 index 00000000..1520b8f6 --- /dev/null +++ b/mea/wifisetup/bluetoothdeviceinfos.h @@ -0,0 +1,61 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Simon Stuerz * + * * + * This file is part of mea * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef BLUETOOTHDEVICEINFOS_H +#define BLUETOOTHDEVICEINFOS_H + +#include +#include + +#include "bluetoothdeviceinfo.h" + +class BluetoothDeviceInfos : public QAbstractListModel +{ + Q_OBJECT +public: + enum BluetoothDeviceInfoRole { + BluetoothDeviceInfoRoleName = Qt::DisplayRole, + BluetoothDeviceInfoRoleAddress, + BluetoothDeviceInfoRoleLe + }; + + explicit BluetoothDeviceInfos(QObject *parent = 0); + + QList deviceInfos(); + + int rowCount(const QModelIndex & parent = QModelIndex()) const; + QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; + + Q_INVOKABLE int count() const; + Q_INVOKABLE BluetoothDeviceInfo *get(int index) const; + + void addBluetoothDeviceInfo(BluetoothDeviceInfo *deviceInfo); + void clearModel(); + +protected: + QHash roleNames() const; + +private: + QList m_deviceInfos; +}; + +#endif // BLUETOOTHDEVICEINFOS_H diff --git a/mea/wifisetup/bluetoothdiscovery.cpp b/mea/wifisetup/bluetoothdiscovery.cpp new file mode 100644 index 00000000..11224ebd --- /dev/null +++ b/mea/wifisetup/bluetoothdiscovery.cpp @@ -0,0 +1,87 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Simon Stuerz * + * * + * This file is part of mea * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "bluetoothdiscovery.h" + +#include + +BluetoothDiscovery::BluetoothDiscovery(QObject *parent) : + QObject(parent), + m_discoveryAgent(new QBluetoothDeviceDiscoveryAgent(this)), + m_deviceInfos(new BluetoothDeviceInfos(this)), + m_discovering(false) +{ + connect(m_discoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &BluetoothDiscovery::deviceDiscovered); + connect(m_discoveryAgent, &QBluetoothDeviceDiscoveryAgent::finished, this, &BluetoothDiscovery::discoveryFinished); + connect(m_discoveryAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error)), this, SLOT(onError(QBluetoothDeviceDiscoveryAgent::Error))); +} + +bool BluetoothDiscovery::discovering() const +{ + return m_discovering; +} + +BluetoothDeviceInfos *BluetoothDiscovery::deviceInfos() +{ + return m_deviceInfos; +} + +void BluetoothDiscovery::setDiscovering(const bool &discovering) +{ + m_discovering = discovering; + emit discoveringChanged(); +} + +void BluetoothDiscovery::deviceDiscovered(const QBluetoothDeviceInfo &deviceInfo) +{ + qDebug() << "Discovery: [+]" << deviceInfo.name() << "(" << deviceInfo.address().toString() << ")" << (deviceInfo.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration ? "LE" : ""); + m_deviceInfos->addBluetoothDeviceInfo(new BluetoothDeviceInfo(deviceInfo)); +} + +void BluetoothDiscovery::discoveryFinished() +{ + qDebug() << "Discovery finished"; + setDiscovering(false); +} + +void BluetoothDiscovery::onError(const QBluetoothDeviceDiscoveryAgent::Error &error) +{ + qWarning() << "Discovery error:" << error << m_discoveryAgent->errorString(); + setDiscovering(false); +} + +void BluetoothDiscovery::start() +{ + if (m_discoveryAgent->isActive()) + m_discoveryAgent->stop(); + + m_deviceInfos->clearModel(); + + m_discoveryAgent->start(); + setDiscovering(true); +} + +void BluetoothDiscovery::stop() +{ + m_discoveryAgent->stop(); + setDiscovering(false); +} diff --git a/mea/wifisetup/bluetoothdiscovery.h b/mea/wifisetup/bluetoothdiscovery.h new file mode 100644 index 00000000..801d09fc --- /dev/null +++ b/mea/wifisetup/bluetoothdiscovery.h @@ -0,0 +1,66 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Simon Stuerz * + * * + * This file is part of mea * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef BLUETOOTHDISCOVERY_H +#define BLUETOOTHDISCOVERY_H + +#include +#include + +#include "bluetoothdeviceinfos.h" + +class BluetoothDiscovery : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool discovering READ discovering NOTIFY discoveringChanged) + Q_PROPERTY(BluetoothDeviceInfos *deviceInfos READ deviceInfos CONSTANT) + +public: + explicit BluetoothDiscovery(QObject *parent = 0); + + bool discovering() const; + + BluetoothDeviceInfos *deviceInfos(); + +private: + QBluetoothDeviceDiscoveryAgent *m_discoveryAgent; + BluetoothDeviceInfos *m_deviceInfos; + + bool m_discovering; + + void setDiscovering(const bool &discovering); + +signals: + void discoveringChanged(); + +private slots: + void deviceDiscovered(const QBluetoothDeviceInfo &deviceInfo); + void discoveryFinished(); + void onError(const QBluetoothDeviceDiscoveryAgent::Error &error); + +public slots: + Q_INVOKABLE void start(); + Q_INVOKABLE void stop(); + +}; + +#endif // BLUETOOTHDISCOVERY_H diff --git a/mea/wifisetup/networkmanagercontroler.cpp b/mea/wifisetup/networkmanagercontroler.cpp new file mode 100644 index 00000000..68fa48b9 --- /dev/null +++ b/mea/wifisetup/networkmanagercontroler.cpp @@ -0,0 +1,68 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Simon Stuerz * + * * + * This file is part of mea * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "networkmanagercontroler.h" + +NetworkManagerControler::NetworkManagerControler(QObject *parent) : QObject(parent) +{ + +} + +QString NetworkManagerControler::name() const +{ + return m_name; +} + +void NetworkManagerControler::setName(const QString &name) +{ + m_name = name; + emit nameChanged(); +} + +QString NetworkManagerControler::address() const +{ + return m_address; +} + +void NetworkManagerControler::setAddress(const QString &address) +{ + m_address = address; +} + +WirelessSetupManager *NetworkManagerControler::manager() +{ + return m_wirelessSetupManager; +} + +void NetworkManagerControler::connectDevice() +{ + if (m_wirelessSetupManager) { + delete m_wirelessSetupManager; + m_wirelessSetupManager = nullptr; + emit managerChanged(); + } + + m_wirelessSetupManager = new WirelessSetupManager(QBluetoothDeviceInfo(QBluetoothAddress(m_address), m_name, 0), this); + emit managerChanged(); + + m_wirelessSetupManager->connectDevice(); +} diff --git a/mea/wifisetup/networkmanagercontroler.h b/mea/wifisetup/networkmanagercontroler.h new file mode 100644 index 00000000..dc5a5f75 --- /dev/null +++ b/mea/wifisetup/networkmanagercontroler.h @@ -0,0 +1,64 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Simon Stuerz * + * * + * This file is part of mea * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef NETWORKMANAGERCONTROLER_H +#define NETWORKMANAGERCONTROLER_H + +#include +#include + +#include "wirelesssetupmanager.h" + +class NetworkManagerControler : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(QString address READ address WRITE setAddress NOTIFY addressChanged) + Q_PROPERTY(WirelessSetupManager *manager READ manager NOTIFY managerChanged) + +public: + explicit NetworkManagerControler(QObject *parent = nullptr); + + QString name() const; + void setName(const QString &name); + + QString address() const; + void setAddress(const QString &address); + + WirelessSetupManager *manager(); + + Q_INVOKABLE void connectDevice(); + +private: + QString m_name; + QString m_address; + + WirelessSetupManager *m_wirelessSetupManager = nullptr; + +signals: + void managerChanged(); + void nameChanged(); + void addressChanged(); + +}; + +#endif // NETWORKMANAGERCONTROLER_H diff --git a/mea/wifisetup/wirelessaccesspoint.cpp b/mea/wifisetup/wirelessaccesspoint.cpp new file mode 100644 index 00000000..30faebad --- /dev/null +++ b/mea/wifisetup/wirelessaccesspoint.cpp @@ -0,0 +1,69 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Simon Stuerz * + * * + * This file is part of mea * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "wirelessaccesspoint.h" + +WirelessAccessPoint::WirelessAccessPoint(QObject *parent): + QObject(parent) +{ + +} + +QString WirelessAccessPoint::ssid() const +{ + return m_ssid; +} + +void WirelessAccessPoint::setSsid(const QString ssid) +{ + m_ssid = ssid; +} + +QString WirelessAccessPoint::macAddress() const +{ + return m_macAddress; +} + +void WirelessAccessPoint::setMacAddress(const QString &macAddress) +{ + m_macAddress = macAddress; +} + +int WirelessAccessPoint::signalStrength() const +{ + return m_signalStrength; +} + +void WirelessAccessPoint::setSignalStrength(const int &signalStrength) +{ + m_signalStrength = signalStrength; +} + +bool WirelessAccessPoint::isProtected() const +{ + return m_isProtected; +} + +void WirelessAccessPoint::setProtected(const bool &isProtected) +{ + m_isProtected = isProtected; +} diff --git a/mea/wifisetup/wirelessaccesspoint.h b/mea/wifisetup/wirelessaccesspoint.h new file mode 100644 index 00000000..b1a9a0ac --- /dev/null +++ b/mea/wifisetup/wirelessaccesspoint.h @@ -0,0 +1,56 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Simon Stuerz * + * * + * This file is part of mea * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef WIRELESSACCESSPOINT_H +#define WIRELESSACCESSPOINT_H + +#include +#include + +class WirelessAccessPoint : public QObject +{ + Q_OBJECT + +public: + WirelessAccessPoint(QObject *parent = 0); + + QString ssid() const; + void setSsid(const QString ssid); + + QString macAddress() const; + void setMacAddress(const QString &macAddress); + + int signalStrength() const; + void setSignalStrength(const int &signalStrength); + + bool isProtected() const; + void setProtected(const bool &isProtected); + +private: + QString m_ssid; + QString m_macAddress; + int m_signalStrength; + bool m_isProtected; + +}; + +#endif // WIRELESSACCESSPOINT_H diff --git a/mea/wifisetup/wirelessaccesspoints.cpp b/mea/wifisetup/wirelessaccesspoints.cpp new file mode 100644 index 00000000..afec6f95 --- /dev/null +++ b/mea/wifisetup/wirelessaccesspoints.cpp @@ -0,0 +1,112 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Simon Stuerz * + * * + * This file is part of mea * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "wirelessaccesspoints.h" + +WirelessAccesspoints::WirelessAccesspoints(QObject *parent) : QAbstractListModel(parent) +{ + +} + +QList WirelessAccesspoints::wirelessAccessPoints() +{ + return m_wirelessAccessPoints; +} + +void WirelessAccesspoints::setWirelessAccessPoints(QList wirelessAccessPoints) +{ + beginResetModel(); + + // Delete all + qDeleteAll(m_wirelessAccessPoints); + m_wirelessAccessPoints.clear(); + + qSort(wirelessAccessPoints.begin(), wirelessAccessPoints.end(), signalStrengthLessThan); + m_wirelessAccessPoints = wirelessAccessPoints; + + endResetModel(); +} + +int WirelessAccesspoints::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return m_wirelessAccessPoints.count(); +} + +QVariant WirelessAccesspoints::data(const QModelIndex &index, int role) const +{ + if (index.row() < 0 || index.row() >= m_wirelessAccessPoints.count()) + return QVariant(); + + WirelessAccessPoint *accessPoint = m_wirelessAccessPoints.at(index.row()); + if (role == WirelessAccesspointRoleSsid) { + return accessPoint->ssid(); + } else if (role == WirelessAccesspointRoleMacAddress) { + return accessPoint->macAddress(); + } else if (role == WirelessAccesspointRoleSignalStrength) { + return accessPoint->signalStrength(); + } else if (role == WirelessAccesspointRoleProtected) { + return accessPoint->isProtected(); + } + + return QVariant(); +} + +int WirelessAccesspoints::count() const +{ + return m_wirelessAccessPoints.count(); +} + +WirelessAccessPoint *WirelessAccesspoints::get(const QString &ssid) const +{ + foreach (WirelessAccessPoint *accessPoint, m_wirelessAccessPoints) { + if (accessPoint->ssid() == ssid) + return accessPoint; + } + + return Q_NULLPTR; +} + +void WirelessAccesspoints::clearModel() +{ + beginResetModel(); + qDeleteAll(m_wirelessAccessPoints); + m_wirelessAccessPoints.clear(); + endResetModel(); +} + +bool WirelessAccesspoints::signalStrengthLessThan(const WirelessAccessPoint *a, const WirelessAccessPoint *b) +{ + return a->signalStrength() > b->signalStrength(); +} + +QHash WirelessAccesspoints::roleNames() const +{ + QHash roles; + roles[WirelessAccesspointRoleSsid] = "ssid"; + roles[WirelessAccesspointRoleMacAddress] = "macAddress"; + roles[WirelessAccesspointRoleSignalStrength] = "signalStrength"; + roles[WirelessAccesspointRoleProtected] = "protected"; + return roles; +} + + diff --git a/mea/wifisetup/wirelessaccesspoints.h b/mea/wifisetup/wirelessaccesspoints.h new file mode 100644 index 00000000..3c89d13e --- /dev/null +++ b/mea/wifisetup/wirelessaccesspoints.h @@ -0,0 +1,66 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Simon Stuerz * + * * + * This file is part of mea * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef WIRELESSACCESSPOINTS_H +#define WIRELESSACCESSPOINTS_H + +#include +#include + +#include "wirelessaccesspoint.h" + +class WirelessAccesspoints : public QAbstractListModel +{ + Q_OBJECT +public: + enum BluetoothDeviceInfoRole { + WirelessAccesspointRoleSsid = Qt::DisplayRole, + WirelessAccesspointRoleMacAddress, + WirelessAccesspointRoleSignalStrength, + WirelessAccesspointRoleProtected + }; + + explicit WirelessAccesspoints(QObject *parent = 0); + + QList wirelessAccessPoints(); + void setWirelessAccessPoints(QList wirelessAccessPoints); + + int rowCount(const QModelIndex & parent = QModelIndex()) const; + QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; + + Q_INVOKABLE int count() const; + Q_INVOKABLE WirelessAccessPoint *get(const QString &ssid) const; + + void clearModel(); + + static bool signalStrengthLessThan(const WirelessAccessPoint *a, const WirelessAccessPoint *b); + +protected: + QHash roleNames() const; + +private: + QList m_wirelessAccessPoints; + + +}; + +#endif // WIRELESSACCESSPOINTS_H diff --git a/mea/wifisetup/wirelesssetupmanager.cpp b/mea/wifisetup/wirelesssetupmanager.cpp new file mode 100644 index 00000000..8adeb74b --- /dev/null +++ b/mea/wifisetup/wirelesssetupmanager.cpp @@ -0,0 +1,961 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Simon Stuerz * + * * + * This file is part of mea * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "wirelesssetupmanager.h" + +#include + +static QBluetoothUuid wifiServiceUuid = QBluetoothUuid(QUuid("e081fec0-f757-4449-b9c9-bfa83133f7fc")); +static QBluetoothUuid wifiCommanderCharacteristicUuid = QBluetoothUuid(QUuid("e081fec1-f757-4449-b9c9-bfa83133f7fc")); +static QBluetoothUuid wifiResponseCharacteristicUuid = QBluetoothUuid(QUuid("e081fec2-f757-4449-b9c9-bfa83133f7fc")); +static QBluetoothUuid wifiStatusCharacteristicUuid = QBluetoothUuid(QUuid("e081fec3-f757-4449-b9c9-bfa83133f7fc")); + +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 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")); + + +WirelessSetupManager::WirelessSetupManager(const QBluetoothDeviceInfo &deviceInfo, QObject *parent) : + BluetoothDevice(deviceInfo, parent), + m_accessPoints(new WirelessAccesspoints(this)) +{ + + connect(this, &WirelessSetupManager::connectedChanged, this, &WirelessSetupManager::onConnectedChanged); + connect(this, &WirelessSetupManager::serviceDiscoveryFinished, this, &WirelessSetupManager::onServiceDiscoveryFinished); +} + +QString WirelessSetupManager::modelNumber() const +{ + return m_modelNumber; +} + +QString WirelessSetupManager::manufacturer() const +{ + return m_manufacturer; +} + +QString WirelessSetupManager::softwareRevision() const +{ + return m_softwareRevision; +} + +QString WirelessSetupManager::firmwareRevision() const +{ + return m_firmwareRevision; +} + +QString WirelessSetupManager::hardwareRevision() const +{ + return m_hardwareRevision; +} + +bool WirelessSetupManager::initialized() const +{ + return m_initialized; +} + +bool WirelessSetupManager::initializing() const +{ + return m_initializing; +} + +bool WirelessSetupManager::working() const +{ + return m_working; +} + +QString WirelessSetupManager::networkStatus() const +{ + return m_networkStatus; +} + +QString WirelessSetupManager::wirelessStatus() const +{ + return m_wirelessStatus; +} + +bool WirelessSetupManager::networkingEnabled() const +{ + return m_networkingEnabled; +} + +bool WirelessSetupManager::wirelessEnabled() const +{ + return m_wirelessEnabled; +} + +WirelessAccesspoints *WirelessSetupManager::accessPoints() +{ + return m_accessPoints; +} + +void WirelessSetupManager::loadNetworks() +{ + qDebug() << "WifiSetupManager: Start loading wifi networks"; + + if (!m_wifiService) { + qWarning() << "WifiSetupManager: Could not send command. Service not valid"; + return; + } + + QLowEnergyCharacteristic characteristic = m_wifiService->characteristic(wifiCommanderCharacteristicUuid); + if (!characteristic.isValid()) { + qWarning() << "WifiSetupManager: Could not send command. Characteristic is not valid"; + return; + } + + m_readingResponse = true; + m_inputDataStream.clear(); + m_accessPoints->clearModel(); + + setStatusText("WifiSetupManager: Loading wifi network list..."); + setWorking(true); + + QVariantMap request; + request.insert("c", (int)WirelessServiceCommandGetNetworks); + streamData(request); +} + +void WirelessSetupManager::loadCurrentConnection() +{ + qDebug() << "WifiSetupManager: Start loading current connection data"; + + if (!m_wifiService) { + qWarning() << "WifiSetupManager: Could not send command. Service not valid"; + return; + } + + QLowEnergyCharacteristic characteristic = m_wifiService->characteristic(wifiCommanderCharacteristicUuid); + if (!characteristic.isValid()) { + qWarning() << "WifiSetupManager: Could not send command. Characteristic is not valid"; + return; + } + + m_readingResponse = false; + m_inputDataStream.clear(); + + setStatusText("WifiSetupManager: Loading current connection data"); + setWorking(true); + + QVariantMap request; + request.insert("c", (int)WirelessServiceCommandGetCurrentConnection); + streamData(request); +} + +void WirelessSetupManager::performWifiScan() +{ + qDebug() << "WifiSetupManager: Start loading wifi networks"; + + if (!m_wifiService) { + qWarning() << "WifiSetupManager: Could not send command. Service not valid"; + return; + } + + QLowEnergyCharacteristic characteristic = m_wifiService->characteristic(wifiCommanderCharacteristicUuid); + if (!characteristic.isValid()) { + qWarning() << "WifiSetupManager: Could not send command. Characteristic is not valid"; + return; + } + + setStatusText("WifiSetupManager: Perform refresh..."); + setWorking(true); + + QVariantMap request; + request.insert("c", (int)WirelessServiceCommandScan); + streamData(request); +} + +void WirelessSetupManager::enableNetworking(bool enable) +{ + qDebug() << "WifiSetupManager: Send networking" << enable; + + if (!m_netwokService) { + qWarning() << "WifiSetupManager: Could not set networking. Service not valid"; + return; + } + + QLowEnergyCharacteristic characteristic = m_netwokService->characteristic(networkCommanderCharacteristicUuid); + if (!characteristic.isValid()) { + qWarning() << "WifiSetupManager: Could not set networking. Characteristic is not valid"; + return; + } + + m_netwokService->writeCharacteristic(characteristic, enable ? QByteArray::fromHex("00") : QByteArray::fromHex("01")); +} + +void WirelessSetupManager::enableWireless(bool enable) +{ + qDebug() << "WifiSetupManager: Send wireless networking" << enable; + + if (!m_netwokService) { + qWarning() << "WifiSetupManager: Could not enable/disable wireless. Service not valid"; + return; + } + + QLowEnergyCharacteristic characteristic = m_netwokService->characteristic(networkCommanderCharacteristicUuid); + if (!characteristic.isValid()) { + qWarning() << "WifiSetupManager: Could not enable/disable wireless. Characteristic is not valid"; + return; + } + + m_netwokService->writeCharacteristic(characteristic, enable ? QByteArray::fromHex("02") : QByteArray::fromHex("03")); +} + +void WirelessSetupManager::connectWirelessNetwork(const QString &ssid, const QString &password) +{ + qDebug() << "WifiSetupManager: Connect wireless network" << ssid << password; + + m_ssid = ssid; + m_password = password; + + if (!m_wifiService) { + qWarning() << "WifiSetupManager: Could not set wireless network. Service not valid"; + return; + } + + QLowEnergyCharacteristic ssidCharacteristic = m_wifiService->characteristic(wifiCommanderCharacteristicUuid); + if (!ssidCharacteristic.isValid()) { + qWarning() << "WifiSetupManager: Could not connect. Characteristic is not valid"; + return; + } + + QVariantMap request; + request.insert("c", (int)WirelessServiceCommandConnect); + QVariantMap parameters; + parameters.insert("e", ssid); + parameters.insert("p", password); + request.insert("p", parameters); + + streamData(request); +} + +void WirelessSetupManager::disconnectWirelessNetwork() +{ + qDebug() << "WifiSetupManager: Disconnect wireless network"; + + if (!m_wifiService) { + qWarning() << "WifiSetupManager: Could not disconnect wireless network. Service not valid"; + return; + } + + QLowEnergyCharacteristic ssidCharacteristic = m_wifiService->characteristic(wifiCommanderCharacteristicUuid); + if (!ssidCharacteristic.isValid()) { + qWarning() << "WifiSetupManager: Could not disconnect wireless ssid. Characteristic is not valid"; + return; + } + + QVariantMap request; + request.insert("c", (int)WirelessServiceCommandDisconnect); + streamData(request); +} + +void WirelessSetupManager::pressPushButton() +{ + qDebug() << "WifiSetupManager: Press push button"; + + if (!m_systemService) { + qWarning() << "WifiSetupManager: Could not press push button. Service not valid"; + return; + } + + QLowEnergyCharacteristic commanderCharacteristic = m_systemService->characteristic(systemCommanderCharacteristicUuid); + if (!commanderCharacteristic.isValid()) { + qWarning() << "WifiSetupManager: Could not press push button. Characteristic is not valid"; + return; + } + + QVariantMap request; + request.insert("c", (int)SystemServiceCommandPushAuthentication); + + QByteArray data = QJsonDocument::fromVariant(request).toJson(QJsonDocument::Compact) + '\n'; + qDebug() << "WifiSetupManager: 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_systemService->writeCharacteristic(commanderCharacteristic, package); + remainingData = remainingData.remove(0, package.count()); + } + + qDebug() << "WifiSetupManager: SystemService: Finished streaming request data"; +} + +void WirelessSetupManager::checkInitialized() +{ + setInitialized(m_deviceInformationService->state() == QLowEnergyService::ServiceDiscovered + && m_netwokService->state() == QLowEnergyService::ServiceDiscovered + && m_wifiService->state() == QLowEnergyService::ServiceDiscovered + && m_systemService->state() == QLowEnergyService::ServiceDiscovered); +} + +void WirelessSetupManager::setModelNumber(const QString &modelNumber) +{ + m_modelNumber = modelNumber; + emit modelNumberChanged(); +} + +void WirelessSetupManager::setManufacturer(const QString &manufacturer) +{ + m_manufacturer = manufacturer; + emit manufacturerChanged(); +} + +void WirelessSetupManager::setSoftwareRevision(const QString &softwareRevision) +{ + m_softwareRevision = softwareRevision; + emit softwareRevisionChanged(); +} + +void WirelessSetupManager::setFirmwareRevision(const QString &firmwareRevision) +{ + m_firmwareRevision = firmwareRevision; + emit firmwareRevisionChanged(); +} + +void WirelessSetupManager::setHardwareRevision(const QString &hardwareRevision) +{ + m_hardwareRevision = hardwareRevision; + emit hardwareRevisionChanged(); +} + +void WirelessSetupManager::setInitializing(bool initializing) +{ + if (m_initializing == initializing) + return; + + qDebug() << "WifiSetupManager:" << (initializing ? "initializing" : "not initializing"); + m_initializing = initializing; + emit initializingChanged(); +} + +void WirelessSetupManager::setInitialized(bool initialized) +{ + if (m_initialized == initialized) + return; + + qDebug() << "WifiSetupManager:" << (initialized ? "initialized" : "not initialized"); + m_initialized = initialized; + emit initializedChanged(); +} + +void WirelessSetupManager::setWorking(bool working) +{ + if (m_working == working) + return; + + qDebug() << "WifiSetupManager:" << (working ? "working" : "not working"); + m_working = working; + emit workingChanged(); +} + +void WirelessSetupManager::setNetworkStatus(int networkStatus) +{ + if (m_networkStatus == networkStatus) + return; + + switch (networkStatus) { + case 0: + m_networkStatus = tr("Unknown"); + break; + case 1: + m_networkStatus = tr("Asleep"); + break; + case 2: + m_networkStatus = tr("Disconnected"); + break; + case 3: + m_networkStatus = tr("Disconnecting..."); + break; + case 4: + m_networkStatus = tr("Connecting..."); + break; + case 5: + m_networkStatus = tr("Connected local."); + break; + case 6: + m_networkStatus = tr("Connected site."); + break; + case 7: + m_networkStatus = tr("Connected global."); + break; + default: + m_networkStatus = tr("-"); + break; + } + + emit networkStatusChanged(); +} + +void WirelessSetupManager::setWirelessStatus(int wirelessStatus) +{ + if (m_wirelessStatus == wirelessStatus) + return; + + switch (wirelessStatus) { + case 0: + m_wirelessStatus = tr("Unknown"); + break; + case 1: + m_wirelessStatus = tr("Unmanaged"); + break; + case 2: + m_wirelessStatus = tr("Unavailable"); + break; + case 3: + m_wirelessStatus = tr("Disconnected"); + break; + case 4: + m_wirelessStatus = tr("Prepare"); + break; + case 5: + m_wirelessStatus = tr("Configure"); + break; + case 6: + m_wirelessStatus = tr("Authentication needed"); + break; + case 7: + m_wirelessStatus = tr("IP configuration"); + break; + case 8: + m_wirelessStatus = tr("IP check"); + break; + case 9: + m_wirelessStatus = tr("Secondaries"); + break; + case 10: + m_wirelessStatus = tr("Connected"); + break; + case 11: + m_wirelessStatus = tr("Deactivating"); + break; + case 12: + m_wirelessStatus = tr("failed"); + break; + default: + m_wirelessStatus = tr("-"); + break; + } + + emit wirelessStatusChanged(); +} + +void WirelessSetupManager::setNetworkingEnabled(bool networkingEnabled) +{ + if (m_networkingEnabled == networkingEnabled) + return; + + qDebug() << "WifiSetupManager: Networking enabled changed" << networkingEnabled; + m_networkingEnabled = networkingEnabled; + emit networkingEnabledChanged(); +} + +void WirelessSetupManager::setWirelessEnabled(bool wirelessEnabled) +{ + if (m_wirelessEnabled == wirelessEnabled) + return; + + qDebug() << "WifiSetupManager: Wireless enabled changed" << wirelessEnabled; + m_wirelessEnabled = wirelessEnabled; + emit wirelessEnabledChanged(); +} + +void WirelessSetupManager::streamData(const QVariantMap &request) +{ + QLowEnergyCharacteristic characteristic = m_wifiService->characteristic(wifiCommanderCharacteristicUuid); + if (!characteristic.isValid()) { + qWarning() << "WifiSetupManager: WirelessService: Wireless commander characteristic not valid"; + return; + } + + QByteArray data = QJsonDocument::fromVariant(request).toJson(QJsonDocument::Compact) + '\n'; + qDebug() << "WifiSetupManager: 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_wifiService->writeCharacteristic(characteristic, package); + remainingData = remainingData.remove(0, package.count()); + } + + qDebug() << "WifiSetupManager: WirelessService: Finished streaming request data"; +} + +void WirelessSetupManager::processNetworkResponse(const QVariantMap &response) +{ + setWorking(false); + + if (!response.contains("c") || !response.contains("r")) { + qWarning() << "WifiSetupManager: Got invalid response map."; + return; + } + + WirelessServiceCommand command = (WirelessServiceCommand)response.value("c").toInt(); + WirelessServiceResponse responseCode = (WirelessServiceResponse)response.value("r").toInt(); + + if (responseCode != WirelessServiceResponseSuccess) { + qWarning() << "WifiSetupManager: Got error for command" << command << responseCode; + return; + } + + qDebug() << "WifiSetupManager: Network command response" << command << responseCode; +} + +void WirelessSetupManager::processWifiResponse(const QVariantMap &response) +{ + setWorking(false); + + if (!response.contains("c") || !response.contains("r")) { + qWarning() << "WifiSetupManager: Got invalid response map."; + return; + } + + WirelessServiceCommand command = (WirelessServiceCommand)response.value("c").toInt(); + WirelessServiceResponse responseCode = (WirelessServiceResponse)response.value("r").toInt(); + + if (responseCode != WirelessServiceResponseSuccess) { + qWarning() << "WifiSetupManager: Got error for command" << command << responseCode; + return; + } + + switch (command) { + case WirelessServiceCommandGetNetworks: { + if (!response.contains("p")) { + qWarning() << "WifiSetupManager: Missing parameters in response."; + return; + } + + QList accessPointsList; + QVariantList accessPointsVariantList = response.value("p").toList(); + foreach (const QVariant &accessPointVariant, accessPointsVariantList) { + QVariantMap accessPointVariantMap = accessPointVariant.toMap(); + WirelessAccessPoint *accessPoint = new WirelessAccessPoint(this); + accessPoint->setSsid(accessPointVariantMap.value("e").toString()); + accessPoint->setMacAddress(accessPointVariantMap.value("m").toString()); + accessPoint->setSignalStrength(accessPointVariantMap.value("s").toInt()); + accessPoint->setProtected(accessPointVariantMap.value("p").toBool()); + accessPointsList.append(accessPoint); + } + + m_accessPoints->setWirelessAccessPoints(accessPointsList); + break; + } + case WirelessServiceCommandConnect: + + break; + case WirelessServiceCommandConnectHidden: + + break; + case WirelessServiceCommandDisconnect: + + break; + case WirelessServiceCommandGetCurrentConnection: + + break; + default: + break; + } +} + +void WirelessSetupManager::processSystemResponse(const QVariantMap &response) +{ + setWorking(false); + + if (!response.contains("c") || !response.contains("r")) { + qWarning() << "WifiSetupManager: Got invalid response map."; + return; + } + + SystemServiceCommand command = (SystemServiceCommand)response.value("c").toInt(); + SystemServiceResponse responseCode = (SystemServiceResponse)response.value("r").toInt(); + + if (responseCode != SystemServiceResponseSuccess) { + qWarning() << "WifiSetupManager: Got error for command" << command << responseCode; + return; + } + + qDebug() << "WifiSetupManager: System command response" << command << responseCode; +} + +void WirelessSetupManager::onConnectedChanged() +{ + if (!connected()) { + // Clean up + qDebug() << "WifiSetupManager: Clean up services"; + m_deviceInformationService->deleteLater(); + m_netwokService->deleteLater(); + m_wifiService->deleteLater(); + m_systemService->deleteLater(); + + m_deviceInformationService = nullptr; + m_netwokService = nullptr; + m_wifiService = nullptr; + m_systemService = nullptr; + + m_accessPoints->clearModel(); + + setInitialized(false); + setInitializing(false); + setWorking(false); + + setManufacturer(""); + setModelNumber(""); + setSoftwareRevision(""); + setFirmwareRevision(""); + setHardwareRevision(""); + } +} + +void WirelessSetupManager::onServiceDiscoveryFinished() +{ + setInitializing(true); + + foreach (const QBluetoothUuid &serviceUuid, controller()->services()) { + qDebug() << "WifiSetupManager: -->" << serviceUuid.toString(); + } + + if (!controller()->services().contains(QBluetoothUuid::DeviceInformation)) { + qWarning() << "WifiSetupManager: Could not find device information service"; + controller()->disconnectFromDevice(); + return; + } + + if (!controller()->services().contains(networkServiceUuid)) { + qWarning() << "WifiSetupManager: Could not find network service"; + controller()->disconnectFromDevice(); + return; + } + + if (!controller()->services().contains(wifiServiceUuid)) { + qWarning() << "WifiSetupManager: Could not find wifi service"; + controller()->disconnectFromDevice(); + return; + } + + // Device Informantion service + if (!m_deviceInformationService) { + m_deviceInformationService = controller()->createServiceObject(QBluetoothUuid::DeviceInformation, this); + if (!m_deviceInformationService) { + qWarning() << "WifiSetupManager: Could not create temperature service."; + controller()->disconnectFromDevice(); + return; + } + + connect(m_deviceInformationService, &QLowEnergyService::stateChanged, this, &WirelessSetupManager::onDeviceInformationStateChanged); + connect(m_deviceInformationService, &QLowEnergyService::characteristicChanged, this, &WirelessSetupManager::onDeviceInformationCharacteristicChanged); + connect(m_deviceInformationService, &QLowEnergyService::characteristicRead, this, &WirelessSetupManager::onDeviceInformationCharacteristicChanged); + + if (m_deviceInformationService->state() == QLowEnergyService::DiscoveryRequired) + m_deviceInformationService->discoverDetails(); + + } + + // Network service + if (!m_netwokService) { + m_netwokService = controller()->createServiceObject(networkServiceUuid, this); + if (!m_netwokService) { + qWarning() << "WifiSetupManager: Could not create network service."; + controller()->disconnectFromDevice(); + return; + } + + connect(m_netwokService, &QLowEnergyService::stateChanged, this, &WirelessSetupManager::onNetworkServiceStateChanged); + connect(m_netwokService, &QLowEnergyService::characteristicChanged, this, &WirelessSetupManager::onNetworkServiceCharacteristicChanged); + connect(m_netwokService, &QLowEnergyService::characteristicRead, this, &WirelessSetupManager::onNetworkServiceReadFinished); + + if (m_netwokService->state() == QLowEnergyService::DiscoveryRequired) + m_netwokService->discoverDetails(); + + } + + // Wifi service + if (!m_wifiService) { + m_wifiService = controller()->createServiceObject(wifiServiceUuid, this); + if (!m_wifiService) { + qWarning() << "WifiSetupManager: Could not create wifi service."; + controller()->disconnectFromDevice(); + return; + } + + connect(m_wifiService, &QLowEnergyService::stateChanged, this, &WirelessSetupManager::onWifiServiceStateChanged); + connect(m_wifiService, &QLowEnergyService::characteristicChanged, this, &WirelessSetupManager::onWifiServiceCharacteristicChanged); + connect(m_wifiService, &QLowEnergyService::characteristicRead, this, &WirelessSetupManager::onWifiServiceReadFinished); + + if (m_wifiService->state() == QLowEnergyService::DiscoveryRequired) + m_wifiService->discoverDetails(); + + } + + // System service + if (!m_systemService) { + m_systemService = controller()->createServiceObject(systemServiceUuid, this); + if (!m_systemService) { + qWarning() << "WifiSetupManager: Could not create system service."; + controller()->disconnectFromDevice(); + return; + } + + connect(m_systemService, &QLowEnergyService::stateChanged, this, &WirelessSetupManager::onWifiServiceStateChanged); + connect(m_systemService, &QLowEnergyService::characteristicChanged, this, &WirelessSetupManager::onWifiServiceCharacteristicChanged); + connect(m_systemService, &QLowEnergyService::characteristicRead, this, &WirelessSetupManager::onWifiServiceReadFinished); + + if (m_systemService->state() == QLowEnergyService::DiscoveryRequired) + m_systemService->discoverDetails(); + + } +} + +void WirelessSetupManager::onDeviceInformationStateChanged(const QLowEnergyService::ServiceState &state) +{ + + if (state != QLowEnergyService::ServiceDiscovered) + return; + + qDebug() << "WifiSetupManager: Device information service discovered."; + + foreach (const QLowEnergyCharacteristic &characteristic, m_deviceInformationService->characteristics()) { + qDebug() << " -->" << characteristic.name() << characteristic.uuid().toString() << characteristic.value(); + foreach (const QLowEnergyDescriptor &descriptor, characteristic.descriptors()) { + qDebug() << " -->" << descriptor.name() << descriptor.uuid().toString() << descriptor.value(); + } + } + + setManufacturer(QString::fromUtf8(m_deviceInformationService->characteristic(QBluetoothUuid::ManufacturerNameString).value())); + setModelNumber(QString::fromUtf8(m_deviceInformationService->characteristic(QBluetoothUuid::ModelNumberString).value())); + setSoftwareRevision(QString::fromUtf8(m_deviceInformationService->characteristic(QBluetoothUuid::SoftwareRevisionString).value())); + setFirmwareRevision(QString::fromUtf8(m_deviceInformationService->characteristic(QBluetoothUuid::FirmwareRevisionString).value())); + setHardwareRevision(QString::fromUtf8(m_deviceInformationService->characteristic(QBluetoothUuid::HardwareRevisionString).value())); + + checkInitialized(); +} + +void WirelessSetupManager::onDeviceInformationCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value) +{ + Q_UNUSED(characteristic) + Q_UNUSED(value) +} + +void WirelessSetupManager::onDeviceInformationReadFinished(const QLowEnergyCharacteristic &characteristic, const QByteArray &value) +{ + Q_UNUSED(characteristic) + Q_UNUSED(value) +} + +void WirelessSetupManager::onNetworkServiceStateChanged(const QLowEnergyService::ServiceState &state) +{ + if (state != QLowEnergyService::ServiceDiscovered) + return; + + qDebug() << "WifiSetupManager: Network service discovered."; + + foreach (const QLowEnergyCharacteristic &characteristic, m_netwokService->characteristics()) { + qDebug() << " -->" << characteristic.name() << characteristic.uuid().toString() << characteristic.value(); + foreach (const QLowEnergyDescriptor &descriptor, characteristic.descriptors()) { + qDebug() << " -->" << descriptor.name() << descriptor.uuid().toString() << descriptor.value(); + } + } + + QLowEnergyCharacteristic networkCharacteristic = m_netwokService->characteristic(networkStatusCharacteristicUuid); + + // Enable notifications + m_netwokService->writeDescriptor(networkCharacteristic.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration), QByteArray::fromHex("0100")); + + setStatusText("Connected and ready"); + setWorking(false); + + // Done with discovery + setNetworkStatus(m_netwokService->characteristic(networkStatusCharacteristicUuid).value().toHex().toUInt(0, 16)); + setNetworkingEnabled((bool)m_netwokService->characteristic(networkingEnabledCharacteristicUuid).value().toHex().toUInt(0, 16)); + setWirelessEnabled((bool)m_netwokService->characteristic(wirelessEnabledCharacteristicUuid).value().toHex().toUInt(0, 16)); + + checkInitialized(); +} + +void WirelessSetupManager::onNetworkServiceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value) +{ + Q_UNUSED(characteristic) + + // Check if currently reading + if (m_readingResponse) { + m_inputDataStream.append(value); + } else { + m_inputDataStream.clear(); + m_readingResponse = 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) { + qWarning() << "Got invalid json object" << m_inputDataStream; + m_inputDataStream.clear(); + m_readingResponse = false; + return; + } + + qDebug() << "Got command stream" << qUtf8Printable(jsonDocument.toJson()); + + processNetworkResponse(jsonDocument.toVariant().toMap()); + + m_inputDataStream.clear(); + m_readingResponse = false; + } +} + +void WirelessSetupManager::onNetworkServiceReadFinished(const QLowEnergyCharacteristic &characteristic, const QByteArray &value) +{ + Q_UNUSED(characteristic) + Q_UNUSED(value) +} + +void WirelessSetupManager::onWifiServiceStateChanged(const QLowEnergyService::ServiceState &state) +{ + if (state != QLowEnergyService::ServiceDiscovered) + return; + + qDebug() << "WifiSetupManager: Wifi service discovered."; + + foreach (const QLowEnergyCharacteristic &characteristic, m_wifiService->characteristics()) { + qDebug() << " -->" << characteristic.name() << characteristic.uuid().toString() << characteristic.value(); + foreach (const QLowEnergyDescriptor &descriptor, characteristic.descriptors()) { + qDebug() << " -->" << descriptor.name() << descriptor.uuid().toString() << descriptor.value(); + } + } + + // Enable notifications + m_wifiService->writeDescriptor(m_wifiService->characteristic(wifiResponseCharacteristicUuid).descriptor(QBluetoothUuid::ClientCharacteristicConfiguration), QByteArray::fromHex("0100")); + m_wifiService->writeDescriptor(m_wifiService->characteristic(wifiStatusCharacteristicUuid).descriptor(QBluetoothUuid::ClientCharacteristicConfiguration), QByteArray::fromHex("0100")); + + setWirelessStatus(m_wifiService->characteristic(wifiStatusCharacteristicUuid).value().toHex().toUInt(0, 16)); + + checkInitialized(); +} + +void WirelessSetupManager::onWifiServiceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value) +{ + Q_UNUSED(characteristic) + + // Check if currently reading + if (m_readingResponse) { + m_inputDataStream.append(value); + } else { + m_inputDataStream.clear(); + m_readingResponse = 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) { + qWarning() << "Got invalid json object" << m_inputDataStream; + m_inputDataStream.clear(); + m_readingResponse = false; + return; + } + + qDebug() << "Got command stream" << qUtf8Printable(jsonDocument.toJson()); + + processWifiResponse(jsonDocument.toVariant().toMap()); + + m_inputDataStream.clear(); + m_readingResponse = false; + } +} + +void WirelessSetupManager::onWifiServiceReadFinished(const QLowEnergyCharacteristic &characteristic, const QByteArray &value) +{ + Q_UNUSED(characteristic) + Q_UNUSED(value) +} + +void WirelessSetupManager::onSystemServiceStateChanged(const QLowEnergyService::ServiceState &state) +{ + if (state != QLowEnergyService::ServiceDiscovered) + return; + + qDebug() << "WifiSetupManager: System service discovered."; + + foreach (const QLowEnergyCharacteristic &characteristic, m_systemService->characteristics()) { + qDebug() << " -->" << characteristic.name() << characteristic.uuid().toString() << characteristic.value(); + foreach (const QLowEnergyDescriptor &descriptor, characteristic.descriptors()) { + qDebug() << " -->" << descriptor.name() << descriptor.uuid().toString() << descriptor.value(); + } + } + + // Enable notifications + m_systemService->writeDescriptor(m_systemService->characteristic(systemResponseCharacteristicUuid).descriptor(QBluetoothUuid::ClientCharacteristicConfiguration), QByteArray::fromHex("0100")); + + checkInitialized(); +} + +void WirelessSetupManager::onSystemServiceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value) +{ + Q_UNUSED(characteristic) + + // Check if currently reading + if (m_readingResponse) { + m_inputDataStream.append(value); + } else { + m_inputDataStream.clear(); + m_readingResponse = 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) { + qWarning() << "Got invalid json object" << m_inputDataStream; + m_inputDataStream.clear(); + m_readingResponse = false; + return; + } + + qDebug() << "Got command stream" << qUtf8Printable(jsonDocument.toJson()); + + processSystemResponse(jsonDocument.toVariant().toMap()); + + m_inputDataStream.clear(); + m_readingResponse = false; + } +} + +void WirelessSetupManager::onSystemServiceReadFinished(const QLowEnergyCharacteristic &characteristic, const QByteArray &value) +{ + Q_UNUSED(characteristic) + Q_UNUSED(value) +} diff --git a/mea/wifisetup/wirelesssetupmanager.h b/mea/wifisetup/wirelesssetupmanager.h new file mode 100644 index 00000000..7b87b763 --- /dev/null +++ b/mea/wifisetup/wirelesssetupmanager.h @@ -0,0 +1,233 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Simon Stuerz * + * * + * This file is part of mea * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef WIRELESSSETUPMANAGER_H +#define WIRELESSSETUPMANAGER_H + +#include +#include + +#include "bluetoothdevice.h" +#include "wirelessaccesspoints.h" + +class WirelessSetupManager : public BluetoothDevice +{ + Q_OBJECT + Q_PROPERTY(bool working READ working NOTIFY workingChanged) + Q_PROPERTY(bool initializing READ initializing NOTIFY initializingChanged) + Q_PROPERTY(bool initialized READ initialized NOTIFY initializedChanged) + + Q_PROPERTY(WirelessAccesspoints *accessPoints READ accessPoints CONSTANT) + + Q_PROPERTY(QString modelNumber READ modelNumber NOTIFY modelNumberChanged) + Q_PROPERTY(QString manufacturer READ manufacturer NOTIFY manufacturerChanged) + Q_PROPERTY(QString softwareRevision READ softwareRevision NOTIFY softwareRevisionChanged) + Q_PROPERTY(QString firmwareRevision READ firmwareRevision NOTIFY firmwareRevisionChanged) + Q_PROPERTY(QString hardwareRevision READ hardwareRevision NOTIFY hardwareRevisionChanged) + + + Q_PROPERTY(QString networkStatus READ networkStatus NOTIFY networkStatusChanged) + Q_PROPERTY(QString wirelessStatus READ wirelessStatus NOTIFY wirelessStatusChanged) + Q_PROPERTY(bool networkingEnabled READ networkingEnabled NOTIFY networkingEnabledChanged) + Q_PROPERTY(bool wirelessEnabled READ wirelessEnabled NOTIFY wirelessEnabledChanged) + +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) + + 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) + + 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 WirelessSetupManager(const QBluetoothDeviceInfo &deviceInfo, QObject *parent = nullptr); + + QString modelNumber() const; + QString manufacturer() const; + QString softwareRevision() const; + QString firmwareRevision() const; + QString hardwareRevision() const; + + bool initialized() const; + bool initializing() const; + bool working() const; + + QString networkStatus() const; + QString wirelessStatus() const; + + bool networkingEnabled() const; + bool wirelessEnabled() const; + + WirelessAccesspoints *accessPoints(); + + // Wireless commands + Q_INVOKABLE void loadNetworks(); + Q_INVOKABLE void loadCurrentConnection(); + Q_INVOKABLE void performWifiScan(); + Q_INVOKABLE void enableNetworking(bool enable); + Q_INVOKABLE void enableWireless(bool enable); + Q_INVOKABLE void connectWirelessNetwork(const QString &ssid, const QString &password = QString()); + Q_INVOKABLE void disconnectWirelessNetwork(); + Q_INVOKABLE void pressPushButton(); + +private: + QLowEnergyService *m_deviceInformationService = nullptr; + QLowEnergyService *m_netwokService = nullptr; + QLowEnergyService *m_wifiService = nullptr; + QLowEnergyService *m_systemService = nullptr; + + WirelessAccesspoints *m_accessPoints = nullptr; + + QString m_modelNumber; + QString m_manufacturer; + QString m_softwareRevision; + QString m_firmwareRevision; + QString m_hardwareRevision; + + bool m_networkingEnabled = false; + bool m_wirelessEnabled = false; + + bool m_working = false; + bool m_initialized = false; + bool m_initializing = false; + + QString m_networkStatus; + QString m_wirelessStatus; + + bool m_readingResponse; + QByteArray m_inputDataStream; + + QString m_ssid; + QString m_password; + + void checkInitialized(); + + // Private set methods for read only properties + void setModelNumber(const QString &modelNumber); + void setManufacturer(const QString &manufacturer); + void setSoftwareRevision(const QString &softwareRevision); + void setFirmwareRevision(const QString &firmwareRevision); + void setHardwareRevision(const QString &hardwareRevision); + + void setInitializing(bool initializing); + void setInitialized(bool initialized); + void setWorking(bool working); + + void setNetworkStatus(int networkStatus); + void setWirelessStatus(int wirelessStatus); + void setNetworkingEnabled(bool networkingEnabled); + void setWirelessEnabled(bool wirelessEnabled); + + // Data methods + void streamData(const QVariantMap &request); + void processNetworkResponse(const QVariantMap &response); + void processWifiResponse(const QVariantMap &response); + void processSystemResponse(const QVariantMap &response); + +signals: + void modelNumberChanged(); + void manufacturerChanged(); + void softwareRevisionChanged(); + void firmwareRevisionChanged(); + void hardwareRevisionChanged(); + + void initializingChanged(); + void initializedChanged(); + void workingChanged(); + + void networkStatusChanged(); + void wirelessStatusChanged(); + void networkingEnabledChanged(); + void wirelessEnabledChanged(); + +private slots: + void onConnectedChanged(); + void onServiceDiscoveryFinished(); + + void onDeviceInformationStateChanged(const QLowEnergyService::ServiceState &state); + void onDeviceInformationCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value); + void onDeviceInformationReadFinished(const QLowEnergyCharacteristic &characteristic, const QByteArray &value); + + void onNetworkServiceStateChanged(const QLowEnergyService::ServiceState &state); + void onNetworkServiceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value); + void onNetworkServiceReadFinished(const QLowEnergyCharacteristic &characteristic, const QByteArray &value); + + void onWifiServiceStateChanged(const QLowEnergyService::ServiceState &state); + void onWifiServiceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value); + void onWifiServiceReadFinished(const QLowEnergyCharacteristic &characteristic, const QByteArray &value); + + void onSystemServiceStateChanged(const QLowEnergyService::ServiceState &state); + void onSystemServiceCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &value); + void onSystemServiceReadFinished(const QLowEnergyCharacteristic &characteristic, const QByteArray &value); + +}; + +#endif // WIRELESSSETUPMANAGER_H diff --git a/translations/mea-en_US.ts b/translations/mea-en_US.ts new file mode 100644 index 00000000..0e2c1863 --- /dev/null +++ b/translations/mea-en_US.ts @@ -0,0 +1,1501 @@ + + + + + ActionDelegateFallback + + + Note: This action type has not been implemented yet + + + + + ActionDelegateNoParams + + + Do it! + + + + + BluetoothDevice + + + Connecting to %1... + + + + + Connected to %1 + + + + + Disconnecting from %1... + + + + + Discovering services of %1... + + + + + %1 connected and discovered. + + + + + %1 disconnected. + + + + + BluetoothDiscoveryPage + + + Bluetooth discovery + + + + + BluetoothLoadingPage + + + Establish bluetooth LE connection + + + + + Initialize services... + + + + + ButtonDevicePage + + + + When this switch is pressed... + + + + + Multiple actions + + + + + %1: Set %2 to %3 + + + + + %1: Call %2 + + + + + No actions configured for this switch. You may add some actions for this switch by using the "Add action" button at the bottom. + + + + + Add an action + + + + + ComposeEventDescriptorPage + + + Select event + + + + + A specific thing + + + + + A group of things + + + + + ConfigureThingPage + + + Delete Thing + + + + + Rename Thing + + + + + Remove device error: + + + + + Thing parameters + + + + + ConnectPage + + + Connect nymea + + + + + Manual connect + + + + + Wireless setup + + + + + Oh, look! + + + + + Uh oh + + + + + There are %1 nymea boxes in your network! Which one would you like to use? + + + + + There doesn't seem to be a nymea box installed in your network. Please make sure your nymea box is correctly set up and connected. + + + + + Searching for nymea boxes... + + + + + Not the ones you're looking for? We're looking for more! + + + + + Manual connect to nymea + + + + + TCP + + + + + Websocket + + + + + Address: + + + + + Port: + + + + + Encrypted connection: + + + + + Connect + + + + + Connecting to your nymea box... + + + + + Cancel + + + + + Warning + + + + + The authenticity of this nymea box cannot be verified. + + + + + If this is the first time you connect to this box, this is expected. Once you trust a box, you should never see this message again for that one. If you see this message multiple times for the same box, something suspicious is going on! + + + + + Fingerprint: + + + + + Do you want to trust this device? + + + + + DevicePageBase + + + Thing is not connected! + + + + + Thing runs out of battery! + + + + + DeviceRulesPage + + + Magic involving %1 + + + + + There's no magic involving %1. + + + + + Add some using the wizard stick! + + + + + DevicesPage + + + All off + + + + + %1 on + + + + + EditDevicesPage + + + Configure Things + + + + + EditRulePage + + + New rule + + + + + Rule name + + + + + This rule is enabled + + + + + Events triggering this rule + + + + + + + %1 - %2 + + + + + Add an event... + + + + + Add another event... + + + + + Conditions to be met + + + + + Add a condition + + + + + Active state enter actions + + + + + Actions to execute + + + + + value from event + + + + + + Add an action... + + + + + + Add another action... + + + + + Active state exit actions + + + + + EditStateEvaluatorPage + + + Conditions + + + + + ErrorDialog + + + Error + + + + + GenericDeviceListPage + + + My %1 things + + + + + All my things + + + + + GenericTypeLogView + + + Data: + + + + + Graph + + + Sorry, there isn't enough data to display a graph here yet! + + + + + InputTriggerDevicePage + + + This event has appeared %1 times in the last 24 hours. + + + + + InterfacesModels + + + Battery level + + + + + Battery level changed + + + + + Battery critical + + + + + Battery critical changed + + + + + LightsDeviceListPage + + + Lights + + + + + All + + + + + off + + + + + LogViewerPage + + + Log viewer + + + + + Time + + + + + Type + + + + + Thing + + + + + Object + + + + + Value + + + + + Nymea Server + + + + + Active changed + + + + + LoginPage + + + Welcome to nymea! + + + + + Sorry, that wasn't right. Try again please. + + + + + The email you've entered isn't valid. + + + + + The password you've chose is too weak. + + + + + An error happened creating the user. + + + + + In order to use your nymea system, please enter your email address and set a password for your nymea box. + + + + + In order to use your nymea system, please log in. + + + + + Your e-mail address: + + + + + Password: + + + + + Confirm password: + + + + + This password isn't long enought to be secure, add some more characters please. + + + + + The passwords don't match. + + + + + OK + + + + + MainPage + + + My things + + + + + Configure things + + + + + Add a new thing + + + + + Magic + + + + + Settings + + + + + System information + + + + + Loading data... + + + + + Welcome to nymea! + + + + + There are no things set up yet. You can start with adding your things by using the menu on the upper left and selecting "Add a new thing". + + + + + NewDeviceWizard + + + Set up new thing + + + + + Searching for things... + + + + + Too bad... + + + + + No things of this kind could be found... + + + + + Make sure your things are set up and connected, try searching again or go back and pick a different kind of thing. + + + + + Try again! + + + + + NewThingMagicPage + + + + When it's freezing... + + + + + When the device runs out of battery... + + + + + When it starts raining... + + + + + Switch light when... + + + + + Dim light when... + + + + + Set light color when... + + + + + Pause playback when... + + + + + Resume playback when... + + + + + Set volume... + + + + + Mute when... + + + + + Unmute when... + + + + + Notify me when... + + + + + NotificationsView + + + Send a notification now: + + + + + Title + + + + + Text + + + + + Send + + + + + NymeaConnection + + + Common Name: + + + + + Oragnisation: + + + + + Locality: + + + + + Oragnisational Unit: + + + + + Country: + + + + + ParamDescriptorDelegateBase + + + + + is + + + + + + + is not + + + + + + is greater + + + + + + is smaller + + + + + + is greater or equal + + + + + + is smaller or equal + + + + + PluginParamsPage + + + %1 settings + + + + + Error saving params: + + + + + PluginsPage + + + Plugins + + + + + PushButtonAuthPage + + + Welcome to nymea! + + + + + Sorry, something went wrong during the setup. Try again please. + + + + + Authentication required + + + + + Please press the button on your nymea box to authenticate this device. + + + + + SelectActionPage + + + Switch lights... + + + + + Control media playback... + + + + + Mute media playback... + + + + + Notify me... + + + + + Manually configure an action... + + + + + Select device + + + + + Select action + + + + + params + + + + + + + OK + + + + + Switch lights + + + + + Set selected lights power to + + + + + Send notification + + + + + Notification text + + + + + SelectEventDescriptorPage + + + When it's freezing... + + + + + When the device runs out of battery... + + + + + When it starts raining... + + + + + SelectEventDescriptorParamsPage + + + Only consider event if + + + + + SelectRuleActionParamsPage + + + Use static value as parameter + + + + + Use event parameter + + + + + OK + + + + + SelectStateDescriptorParamsPage + + + params + + + + + OK + + + + + SelectThingPage + + + Select a thing + + + + + A specific thing + + + + + A group of things + + + + + SensorView + + + 6 h + + + + + 24 h + + + + + 7 d + + + + + SettingsPage + + + Settings + + + + + Application + + + + + View mode + + + + + Windowed + + + + + Maximized + + + + + Fullscreen + + + + + Dark theme + + + + + Return to home on idle + + + + + Graph style + + + + + Bars + + + + + Lines + + + + + System + + + + + Server name + + + + + Debug server enabled + + + + + Open debug interface + + + + + Plugins + + + + + SimpleStateEvaluatorDelegate + + + Press to edit condition + + + + + %1: %2 %3 %4 + + + + + %1 %2: %3 %4 %5%6 + + + + + StateEvaluatorDelegate + + + and all of those + + + + + or any of those + + + + + Add a condition + + + + + StateLogPage + + + History + + + + + %1, %2 has changed %3 times in the last 24h + + + + + SystemInfoPage + + + System information + + + + + Connected to: + + + + + Disconnect + + + + + Log viewer + + + + + WeatherView + + + + N + + + + + NE + + + + + E + + + + + SE + + + + + S + + + + + SW + + + + + W + + + + + NW + + + + + WirelessControlerPage + + + Wireless network + + + + + Network status: + + + + + Wireless status: + + + + + Wireless authentication + + + + + Please enter the password for the Wifi network. + + + + + Connect + + + + + Network manager settings + + + + + Networking + + + + + Wireless networking + + + + + System UUID + + + + + Manufacturer + + + + + Software revision + + + + + Firmware revision + + + + + Hardware revision + + + + + WirelessSetupManager + + + + Unknown + + + + + Asleep + + + + + + Disconnected + + + + + Disconnecting... + + + + + Connecting... + + + + + Connected local. + + + + + Connected site. + + + + + Connected global. + + + + + + - + + + + + Unmanaged + + + + + Unavailable + + + + + Prepare + + + + + Configure + + + + + Authentication needed + + + + + IP configuration + + + + + IP check + + + + + Secondaries + + + + + Connected + + + + + Deactivating + + + + + failed + + + + + main + + + Lighting + + + + + Weather + + + + + Sensors + + + + + Media + + + + + Switches + + + + + Gateways + + + + + Notifications + + + + + Temperature + + + + + Humidity + + + + + Incoming Events + + + + + Events + + + + + Connection error + + + + + Sorry, the version of the nymea box you are trying to connect to is too old. This app requires at least version %1 but the nymea box only supports %2 + + + + + OK + + + +