diff --git a/everest/everest.pro b/everest/everest.pro index 91c7f1c5..8f2f4885 100644 --- a/everest/everest.pro +++ b/everest/everest.pro @@ -4,15 +4,19 @@ QT += network websockets PKGCONFIG += nymea-mqtt SOURCES += \ + jsonrpc/everestjsonrpcclient.cpp \ + jsonrpc/everestjsonrpcinterface.cpp \ + jsonrpc/everestjsonrpcreply.cpp \ mqtt/everestmqtt.cpp \ mqtt/everestmqttclient.cpp \ mqtt/everestmqttdiscovery.cpp \ - integrationplugineverest.cpp \ - jsonrpc/everestjsonrpcclient.cpp + integrationplugineverest.cpp HEADERS += \ + jsonrpc/everestjsonrpcclient.h \ + jsonrpc/everestjsonrpcinterface.h \ + jsonrpc/everestjsonrpcreply.h \ mqtt/everestmqtt.h \ mqtt/everestmqttclient.h \ mqtt/everestmqttdiscovery.h \ - integrationplugineverest.h \ - jsonrpc/everestjsonrpcclient.h + integrationplugineverest.h diff --git a/everest/integrationplugineverest.cpp b/everest/integrationplugineverest.cpp index 8b0ec9d5..1a6e33c8 100644 --- a/everest/integrationplugineverest.cpp +++ b/everest/integrationplugineverest.cpp @@ -41,7 +41,8 @@ IntegrationPluginEverest::IntegrationPluginEverest() void IntegrationPluginEverest::init() { - + EverestJsonRpcClient *client = new EverestJsonRpcClient(this); + client->setSeverUrl(QUrl("ws://10.10.10.165:8080")); } void IntegrationPluginEverest::startMonitoringAutoThings() diff --git a/everest/integrationplugineverest.h b/everest/integrationplugineverest.h index b57a91e1..f4f77a52 100644 --- a/everest/integrationplugineverest.h +++ b/everest/integrationplugineverest.h @@ -35,6 +35,7 @@ #include "extern-plugininfo.h" #include "mqtt/everestmqttclient.h" +#include "jsonrpc/everestjsonrpcclient.h" #include diff --git a/everest/jsonrpc/everestjsonrpcclient.cpp b/everest/jsonrpc/everestjsonrpcclient.cpp index e16e5e71..6e8d6cec 100644 --- a/everest/jsonrpc/everestjsonrpcclient.cpp +++ b/everest/jsonrpc/everestjsonrpcclient.cpp @@ -1,84 +1,109 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2025, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project 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 project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + #include "everestjsonrpcclient.h" #include "extern-plugininfo.h" +#include +#include + EverestJsonRpcClient::EverestJsonRpcClient(QObject *parent) : QObject{parent} { - m_webSocket = new QWebSocket("nymea-client", QWebSocketProtocol::Version13, this); - - connect(m_webSocket, &QWebSocket::disconnected, this, &EverestJsonRpcClient::onDisconnected); - connect(m_webSocket, &QWebSocket::textMessageReceived, this, &EverestJsonRpcClient::onTextMessageReceived); - connect(m_webSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onError(QAbstractSocket::SocketError))); - connect(m_webSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onStateChanged(QAbstractSocket::SocketState))); -} - -EverestJsonRpcClient::~EverestJsonRpcClient() -{ - disconnectServer(); -} - -void EverestJsonRpcClient::sendData(const QByteArray &data) -{ - m_webSocket->sendTextMessage(QString::fromUtf8(data)); -} - -void EverestJsonRpcClient::connectServer(const QUrl &serverUrl) -{ - if (m_connected) { - m_connected = false; - emit connectedChanged(m_connected); - m_webSocket->close(); - } - - m_serverUrl = serverUrl; - - qCDebug(dcEverest()) << "Connecting to" << m_serverUrl.toString(); - m_webSocket->open(m_serverUrl); -} - -void EverestJsonRpcClient::disconnectServer() -{ - qCDebug(dcEverest()) << "Disconnecting from" << m_serverUrl.toString(); - m_webSocket->close(); -} - -void EverestJsonRpcClient::onDisconnected() -{ - qCDebug(dcEverest()) << "Disconnected from" << m_webSocket->requestUrl().toString() << m_webSocket->closeReason(); - - if (m_connected) { - m_connected = false; - emit connectedChanged(m_connected); - } -} - -void EverestJsonRpcClient::onError(QAbstractSocket::SocketError error) -{ - qCDebug(dcEverest()) << "Socket error occurred" << error << m_webSocket->errorString(); -} - -void EverestJsonRpcClient::onStateChanged(QAbstractSocket::SocketState state) -{ - qCDebug(dcEverest()) << "Socket state changed" << state; - - switch (state) { - case QAbstractSocket::ConnectedState: - qCDebug(dcEverest()) << "Connected with" << m_webSocket->requestUrl().toString(); - if (!m_connected) { - m_connected = true; - emit connectedChanged(m_connected); + m_interface = new EverestJsonRpcInterface(this); + connect(m_interface, &EverestJsonRpcInterface::connectedChanged, this, [this](bool connected){ + if (connected) { + EverestJsonRpcReply *reply = hello(); + connect(reply, &EverestJsonRpcReply::finished, this, [this, reply](){ + qCDebug(dcEverest()) << "Reply finished" << m_interface->serverUrl().toString() << reply->method(); + }); + } else { + // Client not available any more } - break; - default: - if (m_connected) { - m_connected = false; - emit connectedChanged(m_connected); - } - break; - } + }); + + connect(m_interface, &EverestJsonRpcInterface::dataReceived, this, &EverestJsonRpcClient::processDataPacket); } -void EverestJsonRpcClient::onTextMessageReceived(const QString &message) +QUrl EverestJsonRpcClient::serverUrl() { - emit dataReceived(message.toUtf8()); + return m_interface->serverUrl(); +} + +void EverestJsonRpcClient::setSeverUrl(const QUrl &serverUrl) +{ + m_interface->connectServer(serverUrl); +} + +EverestJsonRpcReply *EverestJsonRpcClient::hello() +{ + EverestJsonRpcReply *reply = new EverestJsonRpcReply(m_commandId, "API.Hello", QVariantMap(), this); + qCDebug(dcEverest()) << "Calling" << reply->method(); + sendRequest(reply->requestMap()); + m_replies.insert(m_commandId, reply); + m_commandId++; + return reply; +} + +void EverestJsonRpcClient::sendRequest(const QVariantMap &request) +{ + QByteArray data = QJsonDocument::fromVariant(request).toJson(QJsonDocument::Compact) + '\n'; + qCDebug(dcEverest()) << "-->" << m_interface->serverUrl().toString() << qUtf8Printable(data); + m_interface->sendData(data); +} + +void EverestJsonRpcClient::processDataPacket(const QByteArray &data) +{ + qCDebug(dcEverest()) << "<--" << m_interface->serverUrl().toString() << qUtf8Printable(data); + + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); + if (error.error != QJsonParseError::NoError) { + qCWarning(dcEverest()) << "Invalid JSON data recived" << m_interface->serverUrl().toString() << error.errorString(); + return; + } + + QVariantMap dataMap = jsonDoc.toVariant().toMap(); + + int commandId = dataMap.value("id").toInt(); + EverestJsonRpcReply *reply = m_replies.take(commandId); + if (reply) { + qCDebug(dcEverest()) << QString("Got response for %1: %2").arg(reply->method(), QString::fromUtf8(jsonDoc.toJson(QJsonDocument::Indented))); + + // if (dataMap.value("status").toString() == "error") { + // qCWarning(dcEverest()) << "Api error happend" << dataMap.value("error").toString(); + // // FIMXME: handle json layer errors + // } + + reply->setResponse(dataMap); + emit reply->finished(); + return; + } } diff --git a/everest/jsonrpc/everestjsonrpcclient.h b/everest/jsonrpc/everestjsonrpcclient.h index 3e059c94..6ab0fe2b 100644 --- a/everest/jsonrpc/everestjsonrpcclient.h +++ b/everest/jsonrpc/everestjsonrpcclient.h @@ -1,36 +1,71 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2025, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project 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 project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + #ifndef EVERESTJSONRPCCLIENT_H #define EVERESTJSONRPCCLIENT_H #include -#include + +#include "everestjsonrpcreply.h" +#include "everestjsonrpcinterface.h" class EverestJsonRpcClient : public QObject { Q_OBJECT public: explicit EverestJsonRpcClient(QObject *parent = nullptr); - ~EverestJsonRpcClient(); - void sendData(const QByteArray &data); + QUrl serverUrl(); + void setSeverUrl(const QUrl &serverUrl); -public slots: - void connectServer(const QUrl &serverUrl); - void disconnectServer(); + bool available() const; + + EverestJsonRpcReply *hello(); signals: - void connectedChanged(bool connected); - void dataReceived(const QByteArray &data); - -private slots: - void onDisconnected(); - void onError(QAbstractSocket::SocketError error); - void onStateChanged(QAbstractSocket::SocketState state); - void onTextMessageReceived(const QString &message); + void availableChanged(bool available); private: - QWebSocket *m_webSocket = nullptr; - QUrl m_serverUrl; - bool m_connected = false; + bool m_available = false; + + int m_commandId = 0; + EverestJsonRpcInterface *m_interface = nullptr; + + + QHash m_replies; + + void sendRequest(const QVariantMap &request); + +private slots: + void processDataPacket(const QByteArray &data); + }; #endif // EVERESTJSONRPCCLIENT_H diff --git a/everest/jsonrpc/everestjsonrpcinterface.cpp b/everest/jsonrpc/everestjsonrpcinterface.cpp new file mode 100644 index 00000000..506ed001 --- /dev/null +++ b/everest/jsonrpc/everestjsonrpcinterface.cpp @@ -0,0 +1,125 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2025, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project 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 project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "everestjsonrpcinterface.h" +#include "extern-plugininfo.h" + +EverestJsonRpcInterface::EverestJsonRpcInterface(QObject *parent) + : QObject{parent} +{ + m_webSocket = new QWebSocket("nymea-client", QWebSocketProtocol::Version13, this); + + connect(m_webSocket, &QWebSocket::disconnected, this, &EverestJsonRpcInterface::onDisconnected); + connect(m_webSocket, &QWebSocket::textMessageReceived, this, &EverestJsonRpcInterface::onTextMessageReceived); + connect(m_webSocket, &QWebSocket::binaryMessageReceived, this, &EverestJsonRpcInterface::onBinaryMessageReceived); + connect(m_webSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onError(QAbstractSocket::SocketError))); + connect(m_webSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onStateChanged(QAbstractSocket::SocketState))); +} + +EverestJsonRpcInterface::~EverestJsonRpcInterface() +{ + disconnectServer(); +} + +QUrl EverestJsonRpcInterface::serverUrl() const +{ + return m_serverUrl; +} + +void EverestJsonRpcInterface::sendData(const QByteArray &data) +{ + m_webSocket->sendTextMessage(QString::fromUtf8(data)); +} + +void EverestJsonRpcInterface::connectServer(const QUrl &serverUrl) +{ + if (m_connected) { + m_connected = false; + emit connectedChanged(m_connected); + m_webSocket->close(); + } + + m_serverUrl = serverUrl; + + qCDebug(dcEverest()) << "Connecting to" << m_serverUrl.toString(); + m_webSocket->open(m_serverUrl); +} + +void EverestJsonRpcInterface::disconnectServer() +{ + qCDebug(dcEverest()) << "Disconnecting from" << m_serverUrl.toString(); + m_webSocket->close(); +} + +void EverestJsonRpcInterface::onDisconnected() +{ + qCDebug(dcEverest()) << "Disconnected from" << m_webSocket->requestUrl().toString() << m_webSocket->closeReason(); + + if (m_connected) { + m_connected = false; + emit connectedChanged(m_connected); + } +} + +void EverestJsonRpcInterface::onError(QAbstractSocket::SocketError error) +{ + qCDebug(dcEverest()) << "Socket error occurred" << error << m_webSocket->errorString(); +} + +void EverestJsonRpcInterface::onStateChanged(QAbstractSocket::SocketState state) +{ + qCDebug(dcEverest()) << "Socket state changed" << state; + + switch (state) { + case QAbstractSocket::ConnectedState: + qCDebug(dcEverest()) << "Connected with" << m_webSocket->requestUrl().toString(); + if (!m_connected) { + m_connected = true; + emit connectedChanged(m_connected); + } + break; + default: + if (m_connected) { + m_connected = false; + emit connectedChanged(m_connected); + } + break; + } +} + +void EverestJsonRpcInterface::onTextMessageReceived(const QString &message) +{ + emit dataReceived(message.toUtf8()); +} + +void EverestJsonRpcInterface::onBinaryMessageReceived(const QByteArray &message) +{ + emit dataReceived(message); +} diff --git a/everest/jsonrpc/everestjsonrpcinterface.h b/everest/jsonrpc/everestjsonrpcinterface.h new file mode 100644 index 00000000..ef3c2c9e --- /dev/null +++ b/everest/jsonrpc/everestjsonrpcinterface.h @@ -0,0 +1,70 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2025, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project 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 project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef EVERESTJSONRPCINTERFACE_H +#define EVERESTJSONRPCINTERFACE_H + +#include +#include + +class EverestJsonRpcInterface : public QObject +{ + Q_OBJECT +public: + explicit EverestJsonRpcInterface(QObject *parent = nullptr); + ~EverestJsonRpcInterface(); + + QUrl serverUrl() const; + + void sendData(const QByteArray &data); + +public slots: + void connectServer(const QUrl &serverUrl); + void disconnectServer(); + +signals: + void connectedChanged(bool connected); + void dataReceived(const QByteArray &data); + +private slots: + void onDisconnected(); + void onError(QAbstractSocket::SocketError error); + void onStateChanged(QAbstractSocket::SocketState state); + void onTextMessageReceived(const QString &message); + void onBinaryMessageReceived(const QByteArray &message); + +private: + QWebSocket *m_webSocket = nullptr; + QUrl m_serverUrl; + bool m_connected = false; + +}; + +#endif // EVERESTJSONRPCINTERFACE_H diff --git a/everest/jsonrpc/everestjsonrpcreply.cpp b/everest/jsonrpc/everestjsonrpcreply.cpp new file mode 100644 index 00000000..8efb863c --- /dev/null +++ b/everest/jsonrpc/everestjsonrpcreply.cpp @@ -0,0 +1,79 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2025, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project 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 project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "everestjsonrpcreply.h" + +EverestJsonRpcReply::EverestJsonRpcReply(int commandId, QString method, QVariantMap params, QObject *parent) + : QObject{parent}, + m_commandId{commandId}, + m_method{method}, + m_params{params} +{ + +} + +int EverestJsonRpcReply::commandId() const +{ + return m_commandId; +} + + +QString EverestJsonRpcReply::method() const +{ + return m_method; +} + +QVariantMap EverestJsonRpcReply::params() const +{ + return m_params; +} + +QVariantMap EverestJsonRpcReply::requestMap() +{ + QVariantMap request; + request.insert("id", commandId()); + request.insert("jsonrpc", "2.0"); + request.insert("method", method()); + if (!m_params.isEmpty()) + request.insert("params", params()); + + m_commandId++; + return request; +} + +QVariantMap EverestJsonRpcReply::response() const +{ + return m_response; +} + +void EverestJsonRpcReply::setResponse(const QVariantMap &response) +{ + m_response = response; +} diff --git a/everest/jsonrpc/everestjsonrpcreply.h b/everest/jsonrpc/everestjsonrpcreply.h new file mode 100644 index 00000000..150069a6 --- /dev/null +++ b/everest/jsonrpc/everestjsonrpcreply.h @@ -0,0 +1,62 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2025, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project 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 project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef EVERESTJSONRPCREPLY_H +#define EVERESTJSONRPCREPLY_H + +#include +#include + +class EverestJsonRpcReply : public QObject +{ + Q_OBJECT +public: + explicit EverestJsonRpcReply(int commandId, QString method, QVariantMap params = QVariantMap(), QObject *parent = nullptr); + + int commandId() const; + QString method() const; + QVariantMap params() const; + QVariantMap requestMap(); + + QVariantMap response() const; + void setResponse(const QVariantMap &response); + +signals: + void finished(); + +private: + int m_commandId; + QString m_method; + QVariantMap m_params; + QVariantMap m_response; + +}; + +#endif // EVERESTJSONRPCREPLY_H