// SPDX-License-Identifier: GPL-3.0-or-later /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2013 - 2024, nymea GmbH * Copyright (C) 2024 - 2025, chargebyte austria GmbH * * This file is part of nymea-energy-plugin-nymea. * * nymea-energy-plugin-nymea.s free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * nymea-energy-plugin-nymea.s 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with nymea-energy-plugin-nymea. If not, see . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "mockcontroller.h" #include #include #include #include #include "extern-plugininfo.h" EnergyMockController::EnergyMockController(Thing *thing, QObject *parent): QTcpServer(parent), m_thing(thing) { } EnergyMockController::~EnergyMockController() { close(); } void EnergyMockController::incomingConnection(qintptr socket) { QTcpSocket* s = new QTcpSocket(this); connect(s, &QTcpSocket::readyRead, this, &EnergyMockController::onReadyRead); connect(s, &QTcpSocket::disconnected, s, [s](){ s->deleteLater(); }); s->setSocketDescriptor(socket); } void EnergyMockController::logActionExecuted(const ActionType &actionType, const Action &action) { QVariantMap actionMap; actionMap.insert("timestamp", static_cast(QDateTime::currentDateTime().toMSecsSinceEpoch() / 1000)); actionMap.insert("actionTypeId", action.actionTypeId()); actionMap.insert("name", actionType.name()); actionMap.insert("displayName", actionType.displayName()); QVariantList params; foreach (const Param ¶m, action.params()) { QVariantMap paramsMap; paramsMap.insert("paramTypeId", param.paramTypeId()); paramsMap.insert("value", param.value()); paramsMap.insert("name", actionType.paramTypes().findById(param.paramTypeId()).name()); params.append(paramsMap); } actionMap.insert("params", params); m_executedActions.append(actionMap); //qCDebug(dcEnergyMocks()) << "Action logged" << actionMap; } void EnergyMockController::onReadyRead() { QTcpSocket *client = static_cast(sender()); QByteArray data = client->readAll(); // split the data into header and payload int headerEndIndex = data.indexOf("\r\n\r\n"); if (headerEndIndex < 0) { qCWarning(dcEnergyMocks()) << "Could not parse payload" << data; return; } QByteArray rawHeader = data.left(headerEndIndex); QByteArray payload = data.right(data.length() - headerEndIndex).simplified(); // parse status line QStringList headerLines = QString(rawHeader).split(QRegularExpression("\r\n")); QString statusLine = headerLines.takeFirst(); QStringList statusLineTokens = statusLine.split(QRegularExpression("[ \r\n][ \r\n]*")); // verify http version QString httpVersion = statusLineTokens.at(2).toUtf8().simplified(); if (!httpVersion.contains("HTTP")) { qCWarning(dcEnergyMocks()) << "Unknown HTTP version:" << httpVersion; return; } QString methodString = statusLineTokens.at(0).simplified(); QUrl url = QUrl("http://example.com" + statusLineTokens.at(1).simplified()); QUrlQuery query(url); if (url.path() == "/setstates") { qCDebug(dcEnergyMocks()) << "Set states called" << methodString << url.path() << query.toString(); emit updateStateRequestReceived(query); QTextStream os(client); os.setAutoDetectUnicode(true); os << generateHeader(); client->close(); return; } else if (url.path() == "/actionhistory") { QTextStream os(client); os.setAutoDetectUnicode(true); os << generateJsonResponse(QJsonDocument::fromVariant(m_executedActions)); client->close(); return; } else if (url.path() == "/clearactionhistory") { m_executedActions.clear(); QTextStream os(client); os.setAutoDetectUnicode(true); os << generateHeader(); client->close(); return; } // Default website QTextStream os(client); os.setAutoDetectUnicode(true); os << generateWebPage(); client->close(); } QByteArray EnergyMockController::generateHeader() { QByteArray contentHeader( "HTTP/1.0 200 Ok\r\n" "Content-Type: text/html; charset=\"utf-8\"\r\n" "\r\n" ); return contentHeader; } QByteArray EnergyMockController::generateJsonResponse(const QJsonDocument &document) { QByteArray body = document.toJson(QJsonDocument::Compact); QByteArray contentHeader("HTTP/1.0 200 Ok\r\n"); contentHeader.append("Content-Type: application/json\r\n"); contentHeader.append(QString("Content-Length: %1").arg(body.length()).toUtf8() + "\r\n"); contentHeader.append("\r\n"); QByteArray response = contentHeader + body; //qCDebug(dcEnergyMocks()) << "Action histroy response:" << response; return response; } QByteArray EnergyMockController::generateWebPage() { QString body = QString( "" "" "

Mock device Controller

\n" "
" "

Device Information

" "Name: %1
" "ID: %2
" "DeviceClass ID: %3
").arg(m_thing->name()).arg(m_thing->id().toString()).arg(m_thing->thingClassId().toString()); body.append(""); body.append("\n"); return generateHeader() + body.toUtf8(); }