235 lines
8.5 KiB
C++
235 lines
8.5 KiB
C++
#include "devicecontrolapplication.h"
|
|
|
|
#include "engine.h"
|
|
#include "connection/discovery/nymeadiscovery.h"
|
|
#include "connection/nymeahosts.h"
|
|
#include "libnymea-app-core.h"
|
|
#include "../nymea-app/stylecontroller.h"
|
|
#include "../nymea-app/platformhelper.h"
|
|
#include "../nymea-app/nfchelper.h"
|
|
#include "../nymea-app/nfcthingactionwriter.h"
|
|
#include "../nymea-app/platformintegration/android/platformhelperandroid.h"
|
|
|
|
#include <QQmlApplicationEngine>
|
|
#include <QtDebug>
|
|
#include <QtQml>
|
|
#include <QtAndroid>
|
|
#include <QAndroidJniObject>
|
|
#include <QAndroidIntent>
|
|
#include <QNdefNfcUriRecord>
|
|
|
|
QObject *platformHelperProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
|
|
{
|
|
Q_UNUSED(engine)
|
|
Q_UNUSED(scriptEngine)
|
|
return new PlatformHelperAndroid();
|
|
}
|
|
|
|
DeviceControlApplication::DeviceControlApplication(int argc, char *argv[]) : QApplication(argc, argv)
|
|
{
|
|
setApplicationName("nymea-app");
|
|
setOrganizationName("nymea");
|
|
|
|
QSettings settings;
|
|
|
|
m_discovery = new NymeaDiscovery(this);
|
|
|
|
m_engine = new Engine(this);
|
|
|
|
m_qmlEngine = new QQmlApplicationEngine(this);
|
|
|
|
Nymea::Core::registerQmlTypes();
|
|
|
|
qmlRegisterSingletonType<PlatformHelper>("Nymea", 1, 0, "PlatformHelper", platformHelperProvider);
|
|
qmlRegisterSingletonType(QUrl("qrc:///ui/utils/NymeaUtils.qml"), "Nymea", 1, 0, "NymeaUtils" );
|
|
qmlRegisterType<NfcThingActionWriter>("Nymea", 1, 0, "NfcThingActionWriter");
|
|
qmlRegisterSingletonType<NfcHelper>("Nymea", 1, 0, "NfcHelper", NfcHelper::nfcHelperProvider);
|
|
|
|
StyleController *styleController = new StyleController("light", this);
|
|
|
|
QQmlFileSelector *styleSelector = new QQmlFileSelector(m_qmlEngine);
|
|
styleSelector->setExtraSelectors({styleController->currentStyle()});
|
|
|
|
foreach (const QFileInfo &fi, QDir(":/ui/fonts/").entryInfoList()) {
|
|
QFontDatabase::addApplicationFont(fi.absoluteFilePath());
|
|
}
|
|
foreach (const QFileInfo &fi, QDir(":/styles/" + styleController->currentStyle() + "/fonts/").entryInfoList()) {
|
|
qDebug() << "Adding style font:" << fi.absoluteFilePath();
|
|
QFontDatabase::addApplicationFont(fi.absoluteFilePath());
|
|
}
|
|
|
|
qmlRegisterSingletonType(QUrl("qrc:///styles/" + styleController->currentStyle() + "/Style.qml"), "Nymea", 1, 0, "Style" );
|
|
|
|
m_qmlEngine->rootContext()->setContextProperty("styleController", styleController);
|
|
m_qmlEngine->rootContext()->setContextProperty("engine", m_engine);
|
|
m_qmlEngine->rootContext()->setContextProperty("_engine", m_engine);
|
|
m_qmlEngine->rootContext()->setContextProperty("controlledThingId", ""); // Unknown at this point
|
|
|
|
m_qmlEngine->load(QUrl(QLatin1String("qrc:/Main.qml")));
|
|
|
|
jboolean startedByNfc = QtAndroid::androidActivity().callMethod<jboolean>("startedByNfc", "()Z");
|
|
if (startedByNfc) {
|
|
qDebug() << "**** Started by NFC";
|
|
qDebug() << "Registering NFC handler and waiting for message.";
|
|
|
|
QNearFieldManager *manager = new QNearFieldManager(this);
|
|
manager->registerNdefMessageHandler(this, SLOT(handleNdefMessage(QNdefMessage,QNearFieldTarget*)));
|
|
|
|
} else {
|
|
qDebug() << "*** Started by other intent";
|
|
qDebug() << "Expecing nymeaId and thingId in intent extras.";
|
|
QString nymeaId = QtAndroid::androidActivity().callObjectMethod<jstring>("nymeaId").toString();
|
|
QString thingId = QtAndroid::androidActivity().callObjectMethod<jstring>("thingId").toString();
|
|
|
|
connectToNymea(nymeaId);
|
|
m_qmlEngine->rootContext()->setContextProperty("controlledThingId", thingId);
|
|
}
|
|
}
|
|
|
|
void DeviceControlApplication::handleNdefMessage(QNdefMessage message, QNearFieldTarget *target)
|
|
{
|
|
Q_UNUSED(target)
|
|
qDebug() << "************* NFC message!" << message.toByteArray();
|
|
if (message.count() < 1) {
|
|
qWarning() << "NFC message doesn't contain any records...";
|
|
return;
|
|
}
|
|
// NOTE: At this point we're only supporting one NDEF record per message
|
|
QNdefRecord record = message.first();
|
|
QNdefNfcUriRecord uriRecord(record);
|
|
|
|
QUrl url = uriRecord.uri();
|
|
if (url.scheme() != "nymea") {
|
|
qWarning() << "NDEF URI record scheme is not \"nymea://\"";
|
|
return;
|
|
}
|
|
|
|
QUuid nymeaId = QUuid(url.host());
|
|
if (nymeaId.isNull()) {
|
|
qWarning() << "Invalid nymea UUID in NDEF record.";
|
|
return;
|
|
}
|
|
|
|
QUuid thingId = QUuid(QUrlQuery(url).queryItemValue("t"));
|
|
if (thingId.isNull()) {
|
|
qWarning() << "Invalid thing in NDEF record";
|
|
return;
|
|
}
|
|
|
|
m_pendingNfcAction = url;
|
|
|
|
connectToNymea(nymeaId);
|
|
m_qmlEngine->rootContext()->setContextProperty("controlledThingId", thingId);
|
|
|
|
connect(m_engine->thingManager(), &ThingManager::fetchingDataChanged, [this](){
|
|
if (m_engine->jsonRpcClient()->connected() && !m_engine->thingManager()->fetchingData()) {
|
|
qDebug() << "Ready to process commands";
|
|
runNfcAction();
|
|
}
|
|
});
|
|
}
|
|
|
|
void DeviceControlApplication::connectToNymea(const QUuid &nymeaId)
|
|
{
|
|
NymeaHost *host = m_discovery->nymeaHosts()->find(nymeaId);
|
|
if (!host) {
|
|
qWarning() << "No such nymea host:" << nymeaId;
|
|
// TODO: We could wait here until the discovery finds it... But it really should be cached already...
|
|
exit(1);
|
|
}
|
|
qDebug() << "Connecting to:" << host->name();
|
|
m_engine->jsonRpcClient()->connectToHost(host);
|
|
}
|
|
|
|
void DeviceControlApplication::runNfcAction()
|
|
{
|
|
if (!m_pendingNfcAction.isEmpty()) {
|
|
qDebug() << "NFC action:" << m_pendingNfcAction;
|
|
}
|
|
QUrl url = m_pendingNfcAction;
|
|
m_pendingNfcAction.clear();
|
|
|
|
if (url.scheme() != "nymea") {
|
|
qWarning() << "NDEF URI record scheme is not \"nymea://\" in" << url.toString();
|
|
return;
|
|
}
|
|
|
|
QUuid nymeaId = QUuid(url.host());
|
|
if (nymeaId.isNull()) {
|
|
qWarning() << "Invalid nymea UUID" << url.host() << "in NDEF record" << url.toString();
|
|
return;
|
|
}
|
|
|
|
QUuid thingId = QUuid(QUrlQuery(url).queryItemValue("t"));
|
|
Thing *thing = m_engine->thingManager()->things()->getThing(thingId);
|
|
if (!thing) {
|
|
qDebug() << "Thing" << thingId.toString() << "from" << url.toString() << "doesn't exist on nymea host" << nymeaId.toString();
|
|
return;
|
|
}
|
|
|
|
QList<QPair<QString, QString>> queryItems = QUrlQuery(url.query()).queryItems();
|
|
for (int i = 0; i < queryItems.count(); i++) {
|
|
QString entryName = queryItems.at(i).first;
|
|
if (entryName == "t") {
|
|
continue;
|
|
}
|
|
if (!entryName.startsWith("a")) {
|
|
qDebug() << "Only actions are supported. Skipping query item" << entryName;
|
|
continue;
|
|
}
|
|
|
|
QString actionString = queryItems.at(i).second;
|
|
QStringList parts = actionString.split("#");
|
|
if (parts.count() == 0) {
|
|
qDebug() << "Invalid action definition:" << actionString;
|
|
continue;
|
|
}
|
|
|
|
if (parts.count() > 2) {
|
|
// The parameters might contain a #, let's merge them again
|
|
parts[1] = parts.mid(1).join('#');
|
|
}
|
|
|
|
QString actionTypeName = parts.at(0);
|
|
ActionType *actionType = thing->thingClass()->actionTypes()->findByName(actionTypeName);
|
|
if (!actionType) {
|
|
qWarning() << "Invalid action name" << actionType << "in url:" << url.toString();
|
|
continue;
|
|
}
|
|
|
|
QHash<QString, QVariant> paramsInUri;
|
|
if (parts.count() > 1) {
|
|
QString paramsString = parts.at(1);
|
|
foreach (const QString ¶mString, paramsString.split("+")) {
|
|
QStringList parts = paramString.split(":");
|
|
if (parts.count() != 2) {
|
|
qWarning() << "Invalid param format" << paramString << "in url:" << url.toString();
|
|
continue;
|
|
}
|
|
paramsInUri.insert(parts.at(0), parts.at(1));
|
|
}
|
|
}
|
|
|
|
qDebug() << "Parameters in NFC uri:" << paramsInUri;
|
|
|
|
QVariantList params;
|
|
for (int j = 0; j < actionType->paramTypes()->rowCount(); j++) {
|
|
ParamType *paramType = actionType->paramTypes()->get(j);
|
|
QVariantMap param;
|
|
param.insert("paramTypeId", paramType->id());
|
|
if (paramsInUri.contains(paramType->name())) {
|
|
param.insert("value", paramsInUri.value(paramType->name()));
|
|
} else {
|
|
param.insert("value", paramType->defaultValue());
|
|
}
|
|
params.append(param);
|
|
}
|
|
|
|
qDebug() << "Action parameters:" << qUtf8Printable(QJsonDocument::fromVariant(params).toJson());
|
|
|
|
m_engine->thingManager()->executeAction(thingId, actionType->id(), params);
|
|
}
|
|
}
|
|
|
|
|