mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-11-03 21:20:31 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			662 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			662 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
///////////////////////////////////////////////////////////////////////////////////
 | 
						|
// Copyright (C) 2022 Jon Beniston, M7RCE                                        //
 | 
						|
//                                                                               //
 | 
						|
// This program is 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 as version 3 of the License, or                  //
 | 
						|
// (at your option) any later version.                                           //
 | 
						|
//                                                                               //
 | 
						|
// This program 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 General Public License V3 for more details.                               //
 | 
						|
//                                                                               //
 | 
						|
// You should have received a copy of the GNU General Public License             //
 | 
						|
// along with this program. If not, see <http://www.gnu.org/licenses/>.          //
 | 
						|
///////////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
 | 
						|
#include <QDebug>
 | 
						|
#include <QUrl>
 | 
						|
#include <QUrlQuery>
 | 
						|
#include <QNetworkReply>
 | 
						|
#include <QJsonDocument>
 | 
						|
#include <QJsonObject>
 | 
						|
#include <QRegularExpression>
 | 
						|
 | 
						|
#include "util/iot/tplink.h"
 | 
						|
#include "util/simpleserializer.h"
 | 
						|
 | 
						|
const QString TPLinkCommon::m_url = "https://wap.tplinkcloud.com";
 | 
						|
 | 
						|
TPLinkCommon::TPLinkCommon(const QString& username, const QString &password) :
 | 
						|
    m_loggedIn(false),
 | 
						|
    m_outstandingRequest(false),
 | 
						|
    m_username(username),
 | 
						|
    m_password(password),
 | 
						|
    m_networkManager(nullptr)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
void TPLinkCommon::login()
 | 
						|
{
 | 
						|
    QUrl url(m_url);
 | 
						|
 | 
						|
    QNetworkRequest request(url);
 | 
						|
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
 | 
						|
    QJsonObject params {
 | 
						|
        {"appType", "Kasa_Android"},
 | 
						|
        {"cloudUserName", m_username},
 | 
						|
        {"cloudPassword", m_password},
 | 
						|
        {"terminalUUID", "9cc4653e-338f-48e4-b8ca-6ed3f67631e4"}
 | 
						|
    };
 | 
						|
    QJsonObject object {
 | 
						|
        {"method", "login"},
 | 
						|
        {"params", params}
 | 
						|
    };
 | 
						|
    QJsonDocument document;
 | 
						|
    document.setObject(object);
 | 
						|
 | 
						|
    m_networkManager->post(request, document.toJson());
 | 
						|
}
 | 
						|
 | 
						|
void TPLinkCommon::handleLoginReply(QNetworkReply* reply, QString &errorMessage)
 | 
						|
{
 | 
						|
    if (reply)
 | 
						|
    {
 | 
						|
        if (!reply->error())
 | 
						|
        {
 | 
						|
            QJsonDocument document = QJsonDocument::fromJson(reply->readAll());
 | 
						|
            if (document.isObject())
 | 
						|
            {
 | 
						|
                //qDebug() << "Received " << document;
 | 
						|
                if (!m_loggedIn)
 | 
						|
                {
 | 
						|
                    QJsonObject obj = document.object();
 | 
						|
                    if (obj.contains(QStringLiteral("error_code")))
 | 
						|
                    {
 | 
						|
                        int errorCode = obj.value(QStringLiteral("error_code")).toInt();
 | 
						|
                        if (!errorCode)
 | 
						|
                        {
 | 
						|
                            if (obj.contains(QStringLiteral("result")))
 | 
						|
                            {
 | 
						|
                                QJsonObject result = obj.value(QStringLiteral("result")).toObject();
 | 
						|
                                if (result.contains(QStringLiteral("token")))
 | 
						|
                                {
 | 
						|
                                    m_loggedIn = true;
 | 
						|
                                    m_token = result.value(QStringLiteral("token")).toString();
 | 
						|
                                }
 | 
						|
                                else
 | 
						|
                                {
 | 
						|
                                    qDebug() << "TPLinkDevice::handleReply: Object doesn't contain a token: " << result;
 | 
						|
                                }
 | 
						|
                            }
 | 
						|
                            else
 | 
						|
                            {
 | 
						|
                                qDebug() << "TPLinkDevice::handleReply: Object doesn't contain a result object: " << obj;
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                        else
 | 
						|
                        {
 | 
						|
                            qDebug() << "TPLinkDevice::handleReply: Non-zero error_code while logging in: " << errorCode;
 | 
						|
                            if (obj.contains(QStringLiteral("msg")))
 | 
						|
                            {
 | 
						|
                                QString msg = obj.value(QStringLiteral("msg")).toString();
 | 
						|
                                qDebug() << "TPLinkDevice::handleReply: Error message: " << msg;
 | 
						|
                                // Typical msg is "Incorrect email or password"
 | 
						|
                                errorMessage = QString("TP-Link: Failed to log in. %1").arg(msg);
 | 
						|
                            }
 | 
						|
                            else
 | 
						|
                            {
 | 
						|
                                errorMessage = QString("TP-Link: Failed to log in. Error code: %1").arg(errorCode);
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        qDebug() << "TPLinkDevice::handleReply: Object doesn't contain an error_code: " << obj;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                qDebug() << "TPLinkDevice::handleReply: Document is not an object: " << document;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            qDebug() << "TPLinkDevice::handleReply: error: " << reply->error();
 | 
						|
        }
 | 
						|
        reply->deleteLater();
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        qDebug() << "TPLinkDevice::handleReply: reply is null";
 | 
						|
    }
 | 
						|
 | 
						|
    if (!m_loggedIn && errorMessage.isEmpty()) {
 | 
						|
        errorMessage = "TP-Link: Failed to log in.";
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
TPLinkDevice::TPLinkDevice(const QString& username, const QString &password, const QString &deviceId, DeviceDiscoverer::DeviceInfo *info) :
 | 
						|
    Device(info),
 | 
						|
    TPLinkCommon(username, password),
 | 
						|
    m_deviceId(deviceId)
 | 
						|
{
 | 
						|
    m_networkManager = new QNetworkAccessManager();
 | 
						|
    QObject::connect(
 | 
						|
        m_networkManager,
 | 
						|
        &QNetworkAccessManager::finished,
 | 
						|
        this,
 | 
						|
        &TPLinkDevice::handleReply
 | 
						|
    );
 | 
						|
    login();
 | 
						|
}
 | 
						|
 | 
						|
TPLinkDevice::~TPLinkDevice()
 | 
						|
{
 | 
						|
    QObject::disconnect(
 | 
						|
        m_networkManager,
 | 
						|
        &QNetworkAccessManager::finished,
 | 
						|
        this,
 | 
						|
        &TPLinkDevice::handleReply
 | 
						|
    );
 | 
						|
    delete m_networkManager;
 | 
						|
}
 | 
						|
 | 
						|
void TPLinkDevice::getState()
 | 
						|
{
 | 
						|
    if (!m_loggedIn)
 | 
						|
    {
 | 
						|
        m_outstandingRequest = true;
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    QUrl url(m_url);
 | 
						|
 | 
						|
    QNetworkRequest request(url);
 | 
						|
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
 | 
						|
    QJsonObject system;
 | 
						|
    system.insert("get_sysinfo", QJsonValue());
 | 
						|
    QJsonObject emeter;
 | 
						|
    emeter.insert("get_realtime", QJsonValue());
 | 
						|
    QJsonObject requestData {
 | 
						|
        {"system", system},
 | 
						|
        {"emeter", emeter}
 | 
						|
    };
 | 
						|
    QJsonObject params {
 | 
						|
        {"deviceId", m_deviceId},
 | 
						|
        {"requestData", requestData},
 | 
						|
        {"token", m_token}
 | 
						|
    };
 | 
						|
    QJsonObject object {
 | 
						|
        {"method", "passthrough"},
 | 
						|
        {"params", params}
 | 
						|
    };
 | 
						|
    QJsonDocument document;
 | 
						|
    document.setObject(object);
 | 
						|
 | 
						|
    QNetworkReply *reply = m_networkManager->post(request, document.toJson());
 | 
						|
 | 
						|
    recordGetRequest(reply);
 | 
						|
}
 | 
						|
 | 
						|
void TPLinkDevice::setState(const QString &controlId, bool state)
 | 
						|
{
 | 
						|
    if (!m_loggedIn)
 | 
						|
    {
 | 
						|
        // Should we queue these and apply after logged in?
 | 
						|
        qDebug() << "TPLinkDevice::setState: Unable to set state for " << controlId << " to " << state << " as not yet logged in";
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    QUrl url(m_url);
 | 
						|
 | 
						|
    QNetworkRequest request(url);
 | 
						|
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
 | 
						|
    QJsonObject stateObj {
 | 
						|
        {"state", (int)state}
 | 
						|
    };
 | 
						|
    QJsonObject system {
 | 
						|
        {"set_relay_state", stateObj}
 | 
						|
    };
 | 
						|
    QJsonObject requestData {
 | 
						|
        {"system", system}
 | 
						|
    };
 | 
						|
    if (controlId != "switch") {
 | 
						|
        QJsonArray childIds {
 | 
						|
            controlId
 | 
						|
        };
 | 
						|
        QJsonObject context {
 | 
						|
            {"child_ids", childIds}
 | 
						|
        };
 | 
						|
        requestData.insert("context", QJsonValue(context));
 | 
						|
    }
 | 
						|
    QJsonObject params {
 | 
						|
        {"deviceId", m_deviceId},
 | 
						|
        {"requestData", requestData},
 | 
						|
        {"token", m_token}
 | 
						|
    };
 | 
						|
    QJsonObject object {
 | 
						|
        {"method", "passthrough"},
 | 
						|
        {"params", params}
 | 
						|
    };
 | 
						|
    QJsonDocument document;
 | 
						|
    document.setObject(object);
 | 
						|
 | 
						|
    m_networkManager->post(request, document.toJson());
 | 
						|
 | 
						|
    recordSetRequest(controlId);
 | 
						|
}
 | 
						|
 | 
						|
void TPLinkDevice::handleReply(QNetworkReply* reply)
 | 
						|
{
 | 
						|
    if (!m_loggedIn)
 | 
						|
    {
 | 
						|
        QString errorMessage;
 | 
						|
        TPLinkCommon::handleLoginReply(reply, errorMessage);
 | 
						|
        if (!errorMessage.isEmpty())
 | 
						|
        {
 | 
						|
            emit error(errorMessage);
 | 
						|
        }
 | 
						|
        else if (m_outstandingRequest)
 | 
						|
        {
 | 
						|
            m_outstandingRequest = true;
 | 
						|
            getState();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else if (reply)
 | 
						|
    {
 | 
						|
        if (!reply->error())
 | 
						|
        {
 | 
						|
            QByteArray data = reply->readAll();
 | 
						|
            QJsonParseError error;
 | 
						|
            QJsonDocument document = QJsonDocument::fromJson(data, &error);
 | 
						|
            if (!document.isNull())
 | 
						|
            {
 | 
						|
                if (document.isObject())
 | 
						|
                {
 | 
						|
                    //qDebug() << "Received " << document;
 | 
						|
                    QJsonObject obj = document.object();
 | 
						|
                    if (obj.contains(QStringLiteral("result")))
 | 
						|
                    {
 | 
						|
                        QJsonObject resultObj = obj.value(QStringLiteral("result")).toObject();
 | 
						|
                        QHash<QString, QVariant> status;
 | 
						|
 | 
						|
                        if (resultObj.contains(QStringLiteral("responseData")))
 | 
						|
                        {
 | 
						|
                            QJsonObject responseDataObj = resultObj.value(QStringLiteral("responseData")).toObject();
 | 
						|
                            if (responseDataObj.contains(QStringLiteral("system")))
 | 
						|
                            {
 | 
						|
                                QJsonObject systemObj = responseDataObj.value(QStringLiteral("system")).toObject();
 | 
						|
                                if (systemObj.contains(QStringLiteral("get_sysinfo")))
 | 
						|
                                {
 | 
						|
                                    QJsonObject sysInfoObj = systemObj.value(QStringLiteral("get_sysinfo")).toObject();
 | 
						|
                                    if (sysInfoObj.contains(QStringLiteral("child_num")))
 | 
						|
                                    {
 | 
						|
                                        QJsonArray children = sysInfoObj.value(QStringLiteral("children")).toArray();
 | 
						|
                                        for (auto childRef : children)
 | 
						|
                                        {
 | 
						|
                                            QJsonObject childObj = childRef.toObject();
 | 
						|
                                            if (childObj.contains(QStringLiteral("state")) && childObj.contains(QStringLiteral("id")))
 | 
						|
                                            {
 | 
						|
                                                QString id = childObj.value(QStringLiteral("id")).toString();
 | 
						|
                                                if (getAfterSet(reply, id))
 | 
						|
                                                {
 | 
						|
                                                    int state = childObj.value(QStringLiteral("state")).toInt();
 | 
						|
                                                    status.insert(id, state); // key should match id in discoverer
 | 
						|
                                                }
 | 
						|
                                            }
 | 
						|
                                        }
 | 
						|
                                    }
 | 
						|
                                    else if (sysInfoObj.contains(QStringLiteral("relay_state")))
 | 
						|
                                    {
 | 
						|
                                        if (getAfterSet(reply, "switch"))
 | 
						|
                                        {
 | 
						|
                                            int state = sysInfoObj.value(QStringLiteral("relay_state")).toInt();
 | 
						|
                                            status.insert("switch", state); // key should match id in discoverer
 | 
						|
                                        }
 | 
						|
                                    }
 | 
						|
                                }
 | 
						|
                            }
 | 
						|
                            // KP115 has emeter, but KP105 doesn't
 | 
						|
                            if (responseDataObj.contains(QStringLiteral("emeter")))
 | 
						|
                            {
 | 
						|
                                QJsonObject emeterObj = responseDataObj.value(QStringLiteral("emeter")).toObject();
 | 
						|
                                if (emeterObj.contains(QStringLiteral("get_realtime")))
 | 
						|
                                {
 | 
						|
                                    QJsonObject realtimeObj = emeterObj.value(QStringLiteral("get_realtime")).toObject();
 | 
						|
                                    if (realtimeObj.contains(QStringLiteral("current_ma")))
 | 
						|
                                    {
 | 
						|
                                        double current = realtimeObj.value(QStringLiteral("current_ma")).toDouble();
 | 
						|
                                        status.insert("current", current / 1000.0);
 | 
						|
                                    }
 | 
						|
                                    if (realtimeObj.contains(QStringLiteral("voltage_mv")))
 | 
						|
                                    {
 | 
						|
                                        double voltage = realtimeObj.value(QStringLiteral("voltage_mv")).toDouble();
 | 
						|
                                        status.insert("voltage", voltage / 1000.0);
 | 
						|
                                    }
 | 
						|
                                    if (realtimeObj.contains(QStringLiteral("power_mw")))
 | 
						|
                                    {
 | 
						|
                                        double power = realtimeObj.value(QStringLiteral("power_mw")).toDouble();
 | 
						|
                                        status.insert("power", power / 1000.0);
 | 
						|
                                    }
 | 
						|
                                }
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
 | 
						|
                        emit deviceUpdated(status);
 | 
						|
                    }
 | 
						|
                    else if (obj.contains(QStringLiteral("error_code")))
 | 
						|
                    {
 | 
						|
                        // If a device isn't available, we can get:
 | 
						|
                        //    {"error_code":-20002,"msg":"Request timeout"}
 | 
						|
                        //    {"error_code":-20571,"msg":"Device is offline"}
 | 
						|
                        int errorCode = obj.value(QStringLiteral("error_code")).toInt();
 | 
						|
                        QString msg = obj.value(QStringLiteral("msg")).toString();
 | 
						|
                        qDebug() << "TPLinkDevice::handleReply: Error code: " << errorCode << " " << msg;
 | 
						|
 | 
						|
                        emit deviceUnavailable();
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        qDebug() << "TPLinkDevice::handleReply: Object doesn't contain a result or error_code: " << obj;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    qDebug() << "TPLinkDevice::handleReply: Document is not an object: " << document;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                qDebug() << "TPLinkDevice::handleReply: Error parsing JSON: " << error.errorString() << " at offset " << error.offset;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            qDebug() << "TPLinkDevice::handleReply: error: " << reply->error();
 | 
						|
        }
 | 
						|
        removeGetRequest(reply);
 | 
						|
        reply->deleteLater();
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        qDebug() << "TPLinkDevice::handleReply: reply is null";
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
TPLinkDeviceDiscoverer::TPLinkDeviceDiscoverer(const QString& username, const QString &password) :
 | 
						|
    TPLinkCommon(username, password)
 | 
						|
{
 | 
						|
    m_networkManager = new QNetworkAccessManager();
 | 
						|
    QObject::connect(
 | 
						|
        m_networkManager,
 | 
						|
        &QNetworkAccessManager::finished,
 | 
						|
        this,
 | 
						|
        &TPLinkDeviceDiscoverer::handleReply
 | 
						|
    );
 | 
						|
    login();
 | 
						|
}
 | 
						|
 | 
						|
TPLinkDeviceDiscoverer::~TPLinkDeviceDiscoverer()
 | 
						|
{
 | 
						|
    QObject::disconnect(
 | 
						|
        m_networkManager,
 | 
						|
        &QNetworkAccessManager::finished,
 | 
						|
        this,
 | 
						|
        &TPLinkDeviceDiscoverer::handleReply
 | 
						|
    );
 | 
						|
    delete m_networkManager;
 | 
						|
}
 | 
						|
 | 
						|
void TPLinkDeviceDiscoverer::getDevices()
 | 
						|
{
 | 
						|
    if (!m_loggedIn)
 | 
						|
    {
 | 
						|
        m_outstandingRequest = true;
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    QUrl url(m_url);
 | 
						|
 | 
						|
    QNetworkRequest request(url);
 | 
						|
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
 | 
						|
    QJsonObject params {
 | 
						|
        {"token", m_token}
 | 
						|
    };
 | 
						|
    QJsonObject object {
 | 
						|
        {"method", "getDeviceList"},
 | 
						|
        {"params", params}
 | 
						|
    };
 | 
						|
    QJsonDocument document;
 | 
						|
    document.setObject(object);
 | 
						|
 | 
						|
    m_networkManager->post(request, document.toJson());
 | 
						|
}
 | 
						|
 | 
						|
void TPLinkDeviceDiscoverer::getState(const QString &deviceId)
 | 
						|
{
 | 
						|
    QUrl url(m_url);
 | 
						|
 | 
						|
    QNetworkRequest request(url);
 | 
						|
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
 | 
						|
    QJsonObject system;
 | 
						|
    system.insert("get_sysinfo", QJsonValue());
 | 
						|
    QJsonObject emeter;
 | 
						|
    emeter.insert("get_realtime", QJsonValue());
 | 
						|
    QJsonObject requestData {
 | 
						|
        {"system", system},
 | 
						|
        {"emeter", emeter}
 | 
						|
    };
 | 
						|
    QJsonObject params {
 | 
						|
        {"deviceId", deviceId},
 | 
						|
        {"requestData", requestData},
 | 
						|
        {"token", m_token}
 | 
						|
    };
 | 
						|
    QJsonObject object {
 | 
						|
        {"method", "passthrough"},
 | 
						|
        {"params", params}
 | 
						|
    };
 | 
						|
    QJsonDocument document;
 | 
						|
    document.setObject(object);
 | 
						|
 | 
						|
    m_getStateReplies.insert(m_networkManager->post(request, document.toJson()), deviceId);
 | 
						|
}
 | 
						|
 | 
						|
void TPLinkDeviceDiscoverer::handleReply(QNetworkReply* reply)
 | 
						|
{
 | 
						|
    if (!m_loggedIn)
 | 
						|
    {
 | 
						|
        QString errorMessage;
 | 
						|
        TPLinkCommon::handleLoginReply(reply, errorMessage);
 | 
						|
        if (!errorMessage.isEmpty())
 | 
						|
        {
 | 
						|
            emit error(errorMessage);
 | 
						|
        }
 | 
						|
        else if (m_outstandingRequest)
 | 
						|
        {
 | 
						|
            m_outstandingRequest = false;
 | 
						|
            getDevices();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else if (reply)
 | 
						|
    {
 | 
						|
        if (!reply->error())
 | 
						|
        {
 | 
						|
            QByteArray data = reply->readAll();
 | 
						|
            QJsonParseError error;
 | 
						|
            QJsonDocument document = QJsonDocument::fromJson(data, &error);
 | 
						|
            if (!document.isNull())
 | 
						|
            {
 | 
						|
                if (document.isObject())
 | 
						|
                {
 | 
						|
                    //qDebug() << "Received " << document;
 | 
						|
                    QJsonObject obj = document.object();
 | 
						|
 | 
						|
                    if (m_getStateReplies.contains(reply))
 | 
						|
                    {
 | 
						|
                        // Reply for getState
 | 
						|
                        m_getStateReplies.remove(reply);
 | 
						|
                        QJsonObject resultObj = obj.value(QStringLiteral("result")).toObject();
 | 
						|
                        if (resultObj.contains(QStringLiteral("responseData")))
 | 
						|
                        {
 | 
						|
                            QJsonObject responseDataObj = resultObj.value(QStringLiteral("responseData")).toObject();
 | 
						|
                            if (responseDataObj.contains(QStringLiteral("system")))
 | 
						|
                            {
 | 
						|
                                DeviceInfo info;
 | 
						|
                                QJsonObject systemObj = responseDataObj.value(QStringLiteral("system")).toObject();
 | 
						|
                                if (systemObj.contains(QStringLiteral("get_sysinfo")))
 | 
						|
                                {
 | 
						|
                                    QJsonObject sysInfoObj = systemObj.value(QStringLiteral("get_sysinfo")).toObject();
 | 
						|
                                    if (sysInfoObj.contains(QStringLiteral("alias"))) {
 | 
						|
                                        info.m_name = sysInfoObj.value(QStringLiteral("alias")).toString();
 | 
						|
                                    }
 | 
						|
                                    if (sysInfoObj.contains(QStringLiteral("model"))) {
 | 
						|
                                        info.m_model = sysInfoObj.value(QStringLiteral("model")).toString();
 | 
						|
                                    }
 | 
						|
                                    if (sysInfoObj.contains(QStringLiteral("deviceId"))) {
 | 
						|
                                        info.m_id = sysInfoObj.value(QStringLiteral("deviceId")).toString();
 | 
						|
                                    }
 | 
						|
                                    if (sysInfoObj.contains(QStringLiteral("child_num")))
 | 
						|
                                    {
 | 
						|
                                        QJsonArray children = sysInfoObj.value(QStringLiteral("children")).toArray();
 | 
						|
                                        int child = 1;
 | 
						|
                                        for (auto childRef : children)
 | 
						|
                                        {
 | 
						|
                                            QJsonObject childObj = childRef.toObject();
 | 
						|
                                            ControlInfo *controlInfo = new ControlInfo();
 | 
						|
                                            controlInfo->m_id = childObj.value(QStringLiteral("id")).toString();
 | 
						|
                                            if (childObj.contains(QStringLiteral("alias"))) {
 | 
						|
                                                controlInfo->m_name = childObj.value(QStringLiteral("alias")).toString();
 | 
						|
                                            }
 | 
						|
                                            controlInfo->m_type = DeviceDiscoverer::BOOL;
 | 
						|
                                            info.m_controls.append(controlInfo);
 | 
						|
                                            child++;
 | 
						|
                                        }
 | 
						|
                                    }
 | 
						|
                                    else if (sysInfoObj.contains(QStringLiteral("relay_state")))
 | 
						|
                                    {
 | 
						|
                                        ControlInfo *controlInfo = new ControlInfo();
 | 
						|
                                        controlInfo->m_id = "switch";
 | 
						|
                                        if (sysInfoObj.contains(QStringLiteral("alias"))) {
 | 
						|
                                            controlInfo->m_name = sysInfoObj.value(QStringLiteral("alias")).toString();
 | 
						|
                                        }
 | 
						|
                                        controlInfo->m_type = DeviceDiscoverer::BOOL;
 | 
						|
                                        info.m_controls.append(controlInfo);
 | 
						|
                                    }
 | 
						|
                                }
 | 
						|
                                else
 | 
						|
                                {
 | 
						|
                                    qDebug() << "TPLinkDeviceDiscoverer::handleReply: get_sysinfo missing";
 | 
						|
                                }
 | 
						|
                                // KP115 has energy meter, but KP105 doesn't. KP105 will have emeter object, but without get_realtime sub-object
 | 
						|
                                if (responseDataObj.contains(QStringLiteral("emeter")))
 | 
						|
                                {
 | 
						|
                                    QJsonObject emeterObj = responseDataObj.value(QStringLiteral("emeter")).toObject();
 | 
						|
                                    if (emeterObj.contains(QStringLiteral("get_realtime")))
 | 
						|
                                    {
 | 
						|
                                        QJsonObject realtimeObj = emeterObj.value(QStringLiteral("get_realtime")).toObject();
 | 
						|
                                        if (realtimeObj.contains(QStringLiteral("current_ma")))
 | 
						|
                                        {
 | 
						|
                                            SensorInfo *currentSensorInfo = new SensorInfo();
 | 
						|
                                            currentSensorInfo->m_name = "Current";
 | 
						|
                                            currentSensorInfo->m_id = "current";
 | 
						|
                                            currentSensorInfo->m_type = DeviceDiscoverer::FLOAT;
 | 
						|
                                            currentSensorInfo->m_units = "A";
 | 
						|
                                            info.m_sensors.append(currentSensorInfo);
 | 
						|
                                        }
 | 
						|
                                        if (realtimeObj.contains(QStringLiteral("voltage_mv")))
 | 
						|
                                        {
 | 
						|
                                            SensorInfo *voltageSensorInfo = new SensorInfo();
 | 
						|
                                            voltageSensorInfo->m_name = "Voltage";
 | 
						|
                                            voltageSensorInfo->m_id = "voltage";
 | 
						|
                                            voltageSensorInfo->m_type = DeviceDiscoverer::FLOAT;
 | 
						|
                                            voltageSensorInfo->m_units = "V";
 | 
						|
                                            info.m_sensors.append(voltageSensorInfo);
 | 
						|
                                        }
 | 
						|
                                        if (realtimeObj.contains(QStringLiteral("power_mw")))
 | 
						|
                                        {
 | 
						|
                                            SensorInfo *powerSensorInfo = new SensorInfo();
 | 
						|
                                            powerSensorInfo->m_name = "Power";
 | 
						|
                                            powerSensorInfo->m_id = "power";
 | 
						|
                                            powerSensorInfo->m_type = DeviceDiscoverer::FLOAT;
 | 
						|
                                            powerSensorInfo->m_units = "W";
 | 
						|
                                            info.m_sensors.append(powerSensorInfo);
 | 
						|
                                        }
 | 
						|
                                    }
 | 
						|
                                }
 | 
						|
                                if (info.m_controls.size() > 0) {
 | 
						|
                                    m_devices.append(info);
 | 
						|
                                } else {
 | 
						|
                                    qDebug() << "TPLinkDeviceDiscoverer::handleReply: No controls in info";
 | 
						|
                                }
 | 
						|
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                        else
 | 
						|
                        {
 | 
						|
                            qDebug() << "TPLinkDeviceDiscoverer::handleReply: No responseData";
 | 
						|
                        }
 | 
						|
 | 
						|
                        if (m_getStateReplies.size() == 0)
 | 
						|
                        {
 | 
						|
                            emit deviceList(m_devices);
 | 
						|
                            m_devices.clear();
 | 
						|
                        }
 | 
						|
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        // Reply for getDevice
 | 
						|
                        if (obj.contains(QStringLiteral("result")))
 | 
						|
                        {
 | 
						|
                            QJsonObject resultObj = obj.value(QStringLiteral("result")).toObject();
 | 
						|
                            if (resultObj.contains(QStringLiteral("deviceList")))
 | 
						|
                            {
 | 
						|
                                QJsonArray deviceArray = resultObj.value(QStringLiteral("deviceList")).toArray();
 | 
						|
                                for (auto deviceRef : deviceArray)
 | 
						|
                                {
 | 
						|
                                    QJsonObject deviceObj = deviceRef.toObject();
 | 
						|
                                    if (deviceObj.contains(QStringLiteral("deviceId")) && deviceObj.contains(QStringLiteral("deviceType")))
 | 
						|
                                    {
 | 
						|
                                        // In order to discover what controls and sensors a device has, we need to get sysinfo
 | 
						|
                                        getState(deviceObj.value(QStringLiteral("deviceId")).toString());
 | 
						|
                                    }
 | 
						|
                                    else
 | 
						|
                                    {
 | 
						|
                                        qDebug() << "TPLinkDeviceDiscoverer::handleReply: deviceList element doesn't contain a deviceId: " << deviceObj;
 | 
						|
                                    }
 | 
						|
                                }
 | 
						|
                            }
 | 
						|
                            else
 | 
						|
                            {
 | 
						|
                                qDebug() << "TPLinkDeviceDiscoverer::handleReply: result doesn't contain a deviceList: " << resultObj;
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                        else
 | 
						|
                        {
 | 
						|
                            qDebug() << "TPLinkDeviceDiscoverer::handleReply: Object doesn't contain a result: " << obj;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    qDebug() << "TPLinkDeviceDiscoverer::handleReply: Document is not an object: " << document;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                qDebug() << "TPLinkDeviceDiscoverer::handleReply: Error parsing JSON: " << error.errorString() << " at offset " << error.offset;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            qDebug() << "TPLinkDeviceDiscoverer::handleReply: error: " << reply->error();
 | 
						|
        }
 | 
						|
        reply->deleteLater();
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        qDebug() << "TPLinkDeviceDiscoverer::handleReply: reply is null";
 | 
						|
    }
 | 
						|
}
 |