diff --git a/plugins/deviceplugins/tune/deviceplugintune.cpp b/plugins/deviceplugins/tune/deviceplugintune.cpp index 7ddbedf2..276546b1 100644 --- a/plugins/deviceplugins/tune/deviceplugintune.cpp +++ b/plugins/deviceplugins/tune/deviceplugintune.cpp @@ -26,10 +26,12 @@ DevicePluginTune::DevicePluginTune() { - m_manager = new TuneManager(31337, this); + m_server = new JsonRpcServer(this); - connect(m_manager, &TuneManager::tuneConnectionStatusChanged, this, &DevicePluginTune::tuneConnectionStatusChanged); - connect(m_manager, &TuneManager::dataReady, this, &DevicePluginTune::tuneDataAvailable); + connect(m_server, &JsonRpcServer::connectionStatusChanged, this, &DevicePluginTune::tuneConnectionStatusChanged); + connect(m_server, &JsonRpcServer::gotMoodSync, this, &DevicePluginTune::updateMood); + connect(m_server, &JsonRpcServer::gotTuneSync, this, &DevicePluginTune::updateTune); + connect(m_server, &JsonRpcServer::gotActionResponse, this, &DevicePluginTune::processActionResponse); } DeviceManager::HardwareResources DevicePluginTune::requiredHardware() const @@ -39,12 +41,11 @@ DeviceManager::HardwareResources DevicePluginTune::requiredHardware() const void DevicePluginTune::startMonitoringAutoDevices() { - m_manager->start(); + m_server->start(); } DeviceManager::DeviceSetupStatus DevicePluginTune::setupDevice(Device *device) { - // tune if (device->deviceClassId() == tuneDeviceClassId && !tuneAlreadyAdded()) { m_tuneDeviceId = device->id(); @@ -98,88 +99,14 @@ void DevicePluginTune::deviceRemoved(Device *device) } sync(); } - if (device->deviceClassId() == tuneDeviceClassId && m_manager->tuneAvailable()) { + if (device->deviceClassId() == tuneDeviceClassId && m_server->tuneAvailable()) { tuneAutodetected(); } } bool DevicePluginTune::sync() { - // sync with devices with tune - if (!m_manager->tuneAvailable()) { - return false; - } - - QVariantMap message; - QVariantList moods; - QVariantMap tune; - foreach (Device* device, myDevices()) { - if (device->deviceClassId() == moodDeviceClassId) { - QVariantMap mood; - mood.insert("name", device->paramValue("name")); - mood.insert("deviceId", device->id()); - mood.insert("position", device->paramValue("position")); - mood.insert("icon", device->paramValue("icon")); - QVariantMap states; - states.insert("value", device->stateValue(valueStateTypeId).toInt()); - states.insert("active", device->stateValue(activeStateTypeId).toBool()); - mood.insert("states", states); - moods.append(mood); - } - if (device->deviceClassId() == tuneDeviceClassId) { - tune.insert("name", device->paramValue("name")); - tune.insert("deviceId", device->id()); - QVariantMap states; - states.insert("value", device->stateValue(brightnessStateTypeId).toInt()); - states.insert("active", device->stateValue(powerStateTypeId).toBool()); - tune.insert("states", states); - } - } - - message.insert("method", "Items.Sync"); - message.insert("moods", moods); - message.insert("tune", tune); - - QJsonDocument jsonDoc = QJsonDocument::fromVariant(message); - QByteArray data = jsonDoc.toJson(QJsonDocument::Compact); - - qDebug() << jsonDoc.toJson(); - - m_manager->sendData(data); - return true; -} - -void DevicePluginTune::syncStates(Device *device) -{ - QVariantMap message; - - if (device->deviceClassId() == moodDeviceClassId) { - QVariantMap mood; - QVariantMap states; - states.insert("value", device->stateValue(valueStateTypeId).toInt()); - states.insert("active", device->stateValue(activeStateTypeId).toBool()); - mood.insert("states", states); - mood.insert("deviceId", device->id()); - message.insert("method", "Items.SyncStates"); - message.insert("mood", mood); - } - - if (device->deviceClassId() == tuneDeviceClassId) { - QVariantMap tune; - QVariantMap states; - states.insert("value", device->stateValue(brightnessStateTypeId).toInt()); - states.insert("active", device->stateValue(powerStateTypeId).toBool()); - tune.insert("states", states); - tune.insert("deviceId", device->id()); - message.insert("method", "Items.SyncStates"); - message.insert("tune", tune); - } - - QJsonDocument jsonDoc = QJsonDocument::fromVariant(message); - QByteArray data = jsonDoc.toJson(QJsonDocument::Compact); - - m_manager->sendData(data); - + return m_server->sync(myDevices()); } bool DevicePluginTune::tuneAlreadyAdded() @@ -203,21 +130,6 @@ void DevicePluginTune::tuneAutodetected() metaObject()->invokeMethod(this, "autoDevicesAppeared", Qt::QueuedConnection, Q_ARG(DeviceClassId, tuneDeviceClassId), Q_ARG(QList, descriptorList)); } -void DevicePluginTune::activateMood(Device *device) -{ - // first deactivate every current active mood - foreach (Device* d, myDevices()) { - if (d->deviceClassId() == moodDeviceClassId) { - if (d->stateValue(activeStateTypeId).toBool()) { - d->setStateValue(activeStateTypeId, false); - syncStates(d); - } - } - } - device->setStateValue(activeStateTypeId, true); - syncStates(device); -} - void DevicePluginTune::tuneConnectionStatusChanged(const bool &connected) { if (connected) { @@ -234,87 +146,60 @@ void DevicePluginTune::tuneConnectionStatusChanged(const bool &connected) } } -void DevicePluginTune::tuneDataAvailable(const QByteArray &data) +void DevicePluginTune::updateMood(const QVariantMap &message) { - QJsonParseError error; - QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); - - if(error.error != QJsonParseError::NoError) { - qDebug() << "failed to parse data" << data << ":" << error.errorString(); + QVariantMap mood = message.value("mood").toMap(); + Device *device = deviceManager()->findConfiguredDevice(DeviceId(mood.value("deviceId").toString())); + if (device) { + QVariantMap states = mood.value("states").toMap(); + device->setStateValue(activeStateTypeId, states.value("active").toBool()); + device->setStateValue(valueStateTypeId, states.value("value").toInt()); } +} - qDebug() << jsonDoc.toJson(); +void DevicePluginTune::updateTune(const QVariantMap &message) +{ + QVariantMap tune = message.value("tune").toMap(); + Device *device = deviceManager()->findConfiguredDevice(DeviceId(tune.value("deviceId").toString())); + if (device) { + QVariantMap states = tune.value("states").toMap(); + device->setStateValue(powerStateTypeId, states.value("power").toBool()); + device->setStateValue(brightnessStateTypeId, states.value("brigthness").toInt()); + device->setStateValue(approximationDetectedStateTypeId, states.value("approximationDetected").toBool()); + device->setStateValue(lightIntensityStateTypeId, states.value("lightIntensity").toInt()); + device->setStateValue(humidityStateTypeId, states.value("humidity").toInt()); + device->setStateValue(temperatureStateTypeId, states.value("temperature").toDouble()); + } +} - QVariantMap message = jsonDoc.toVariant().toMap(); - if (message.value("method").toString() == "Items.SyncStates") { - if (message.contains("mood")) { - QVariantMap mood = message.value("mood").toMap(); - Device *device = deviceManager()->findConfiguredDevice(DeviceId(mood.value("deviceId").toString())); - if (device) { - qDebug() << "update device" << device->name(); - QVariantMap states = mood.value("states").toMap(); - bool activeValue = states.value("active").toBool(); - int value = states.value("value").toInt(); - if (activeValue) { - activateMood(device); - } else { - device->setStateValue(activeStateTypeId, activeValue); - } - device->setStateValue(valueStateTypeId, value); - } - } - if (message.contains("tune")) { - QVariantMap tune = message.value("tune").toMap(); - Device *device = deviceManager()->findConfiguredDevice(DeviceId(tune.value("deviceId").toString())); - if (device) { - QVariantMap states = tune.value("states").toMap(); - device->setStateValue(powerStateTypeId, states.value("active").toBool()); - device->setStateValue(brightnessStateTypeId, states.value("value").toInt()); - device->setStateValue(approximationDetectedStateTypeId, states.value("approximationDetected").toBool()); - device->setStateValue(lightIntensityStateTypeId, states.value("lightIntensity").toInt()); - device->setStateValue(humidityStateTypeId, states.value("humidity").toInt()); - device->setStateValue(temperatureStateTypeId, states.value("temperature").toDouble()); - } - } +void DevicePluginTune::processActionResponse(const QVariantMap &message) +{ + bool success = message.value("success").toBool(); + ActionId actionId = ActionId(message.value("params").toMap().value("actionId").toString()); + if (success) { + emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorNoError); + } else { + emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorHardwareFailure); } } DeviceManager::DeviceError DevicePluginTune::executeAction(Device *device, const Action &action) { - if (!m_manager->tuneAvailable()) { + if (!m_server->tuneAvailable()) { return DeviceManager::DeviceErrorHardwareNotAvailable; } - // Mood - if (device->deviceClassId() == moodDeviceClassId) { - if (action.actionTypeId() == activeActionTypeId) { - bool currentState = device->stateValue(activeStateTypeId).toBool(); - device->setStateValue(activeStateTypeId, !currentState); - syncStates(device); - return DeviceManager::DeviceErrorNoError; - } - if (action.actionTypeId() == valueActionTypeId) { - device->setStateValue(valueStateTypeId, action.param("percentage").value().toInt()); - syncStates(device); - return DeviceManager::DeviceErrorNoError; - } + if (device->deviceClassId() != moodDeviceClassId || device->deviceClassId() != tuneDeviceClassId) { + return DeviceManager::DeviceErrorDeviceClassNotFound; + } + + if (action.actionTypeId() != powerActionTypeId || + action.actionTypeId() != brightnessActionTypeId || + action.actionTypeId() != valueActionTypeId){ return DeviceManager::DeviceErrorActionTypeNotFound; } - if (device->deviceClassId() == tuneDeviceClassId) { - if (action.actionTypeId() == powerActionTypeId) { - bool currentState = device->stateValue(powerStateTypeId).toBool(); - device->setStateValue(powerStateTypeId, !currentState); - syncStates(device); - return DeviceManager::DeviceErrorNoError; - } - if (action.actionTypeId() == brightnessActionTypeId) { - device->setStateValue(brightnessStateTypeId, action.param("brightness").value().toInt()); - syncStates(device); - return DeviceManager::DeviceErrorNoError; - } - } - - return DeviceManager::DeviceErrorDeviceClassNotFound; + m_server->executeAction(device, action); + return DeviceManager::DeviceErrorAsync; } diff --git a/plugins/deviceplugins/tune/deviceplugintune.h b/plugins/deviceplugins/tune/deviceplugintune.h index 6157dd74..c0c4a6a5 100644 --- a/plugins/deviceplugins/tune/deviceplugintune.h +++ b/plugins/deviceplugins/tune/deviceplugintune.h @@ -20,7 +20,9 @@ #define DEVICEPLUGINTUNE_H #include "plugin/deviceplugin.h" -#include "tunemanager.h" +#include "jsonrpcserver.h" + +class JsonRpcServer; class DevicePluginTune : public DevicePlugin { @@ -39,18 +41,18 @@ public: void deviceRemoved(Device *device) override; private: - TuneManager *m_manager; + JsonRpcServer *m_server; DeviceId m_tuneDeviceId; bool sync(); - void syncStates(Device *device); bool tuneAlreadyAdded(); void tuneAutodetected(); - void activateMood(Device* device); private slots: void tuneConnectionStatusChanged(const bool &connected); - void tuneDataAvailable(const QByteArray &data); + void updateMood(const QVariantMap &message); + void updateTune(const QVariantMap &message); + void processActionResponse(const QVariantMap &message); public slots: DeviceManager::DeviceError executeAction(Device *device, const Action &action) override; diff --git a/plugins/deviceplugins/tune/jsonrpcserver.cpp b/plugins/deviceplugins/tune/jsonrpcserver.cpp new file mode 100644 index 00000000..984242d0 --- /dev/null +++ b/plugins/deviceplugins/tune/jsonrpcserver.cpp @@ -0,0 +1,232 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This file is part of guh. * + * * + * Guh 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, version 2 of the License. * + * * + * Guh 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 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with guh. If not, see . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "jsonrpcserver.h" + +extern PluginId pluginId; +extern DeviceClassId moodDeviceClassId; +extern StateTypeId activeStateTypeId; +extern ActionTypeId activeActionTypeId; +extern StateTypeId valueStateTypeId; +extern ActionTypeId valueActionTypeId; +extern DeviceClassId tuneDeviceClassId; +extern StateTypeId reachableStateTypeId; +extern StateTypeId approximationDetectedStateTypeId; +extern StateTypeId temperatureStateTypeId; +extern StateTypeId humidityStateTypeId; +extern StateTypeId lightIntensityStateTypeId; +extern StateTypeId powerStateTypeId; +extern ActionTypeId powerActionTypeId; +extern StateTypeId brightnessStateTypeId; +extern ActionTypeId brightnessActionTypeId; + +JsonRpcServer::JsonRpcServer(QObject *parent) : + QObject(parent), + m_id(0) +{ + m_manager = new TuneManager(31337, this); + + connect(m_manager, &TuneManager::tuneConnectionStatusChanged, this, &JsonRpcServer::connectionStatusChanged); + connect(m_manager, &TuneManager::dataReady, this, &JsonRpcServer::processData); +} + +void JsonRpcServer::start() +{ + m_manager->start(); +} + +void JsonRpcServer::stop() +{ + m_manager->stop(); +} + +bool JsonRpcServer::tuneAvailable() +{ + return m_manager->tuneAvailable(); +} + +bool JsonRpcServer::sync(QList deviceList) +{ + if (!tuneAvailable()) { + return false; + } + + QVariantMap message; + QVariantMap params; + QVariantList moods; + QVariantMap tune; + foreach (Device* device, deviceList) { + if (device->deviceClassId() == moodDeviceClassId) { + QVariantMap mood; + mood.insert("name", device->paramValue("name")); + mood.insert("deviceId", device->id()); + mood.insert("position", device->paramValue("position")); + mood.insert("icon", device->paramValue("icon")); + QVariantMap states; + states.insert("value", device->stateValue(valueStateTypeId).toInt()); + states.insert("active", device->stateValue(activeStateTypeId).toBool()); + mood.insert("states", states); + moods.append(mood); + } + if (device->deviceClassId() == tuneDeviceClassId) { + tune.insert("name", device->paramValue("name")); + tune.insert("deviceId", device->id()); + tune.insert("brightness", device->stateValue(brightnessStateTypeId).toInt()); + tune.insert("power", device->stateValue(powerStateTypeId).toBool()); + } + } + m_id++; + params.insert("moods", moods); + params.insert("tune", tune); + message.insert("method", "Items.Sync"); + message.insert("id", m_id); + message.insert("params", params); + + m_requests.insert(m_id, message); + + QJsonDocument jsonDoc = QJsonDocument::fromVariant(message); + qDebug() << jsonDoc.toJson(); + m_manager->sendData(jsonDoc.toJson(QJsonDocument::Compact)); + return true; +} + +void JsonRpcServer::executeAction(Device *device, const Action &action) +{ + QVariantMap message; + QVariantMap params; + + params.insert("deviceId", device->deviceClassId()); + params.insert("actionId", action.id()); + + if (device->deviceClassId() == moodDeviceClassId) { + message.insert("method", "Mood.ExecuteAction"); + if (action.actionTypeId() == valueActionTypeId) { + params.insert("value", action.param("percentage").value().toInt()); + params.insert("active", device->stateValue(activeStateTypeId).toBool()); + } else if (action.actionTypeId() == activeActionTypeId) { + params.insert("value", device->stateValue(valueStateTypeId).toInt()); + params.insert("active", action.param("active").value().toBool()); + } + } else if(device->deviceClassId() == tuneDeviceClassId) { + message.insert("method", "Tune.ExecuteAction"); + if (action.actionTypeId() == valueActionTypeId) { + params.insert("brightness", action.param("brightness").value().toInt()); + params.insert("power", device->stateValue(powerStateTypeId).toBool()); + } else if (action.actionTypeId() == activeActionTypeId) { + params.insert("brightness", device->stateValue(brightnessStateTypeId).toInt()); + params.insert("power", action.param("power").value().toBool()); + } + } + + m_id++; + message.insert("id", m_id); + message.insert("params", params); + + m_requests.insert(m_id, message); + + QJsonDocument jsonDoc = QJsonDocument::fromVariant(message); + qDebug() << jsonDoc.toJson(); + m_manager->sendData(jsonDoc.toJson(QJsonDocument::Compact)); +} + +QByteArray JsonRpcServer::formatResponse(int commandId, const QVariantMap &responseParams) +{ + QVariantMap responseMap; + responseMap.insert("id", commandId); + responseMap.insert("success", true); + responseMap.insert("params", responseParams); + QJsonDocument jsonDoc = QJsonDocument::fromVariant(responseMap); + return jsonDoc.toJson(QJsonDocument::Compact); +} + +QByteArray JsonRpcServer::formatErrorResponse(int commandId, const QString &errorMessage) +{ + QVariantMap responseMap; + responseMap.insert("id", commandId); + responseMap.insert("success", false); + responseMap.insert("error", errorMessage); + QJsonDocument jsonDoc = QJsonDocument::fromVariant(responseMap); + return jsonDoc.toJson(QJsonDocument::Compact); +} + +void JsonRpcServer::handleResponse(const QVariantMap &response) +{ + int responseId = response.value("id").toInt(); + + if (!m_requests.contains(responseId)) { + qWarning() << "ERROR: got a response without a corresponding request!!!!"; + return; + } + + // remove it request since we have a response now... + QVariantMap request = m_requests.take(responseId); + + // Note: maby we have to do something if any request fails + if (!response.value("success").toBool()) { + qWarning() << "ERROR: for request:" << request << response.value("error").toString(); + } + + // check if this is a response to an action execution + if (response.contains("actionId")) { + emit gotActionResponse(response); + } +} + +void JsonRpcServer::processData(const QByteArray &data) +{ + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); + + if(error.error != QJsonParseError::NoError) { + qDebug() << "failed to parse data" << data << ":" << error.errorString(); + return; + } + + QVariantMap message = jsonDoc.toVariant().toMap(); + + // check if the message has an id + if (!message.contains("id")) { + qWarning() << "ERROR: message does not contain a valid id" << message; + return; + } + + // if this is a response message to a request + if (message.contains("success") && !message.contains("method")) { + handleResponse(message); + return; + } + + // otherwise we need a method + if (!message.contains("method")) { + qWarning() << "ERROR: message does not contain a valid method" << message; + return; + } + + int commandId = message.value("id").toInt(); + + if (message.value("method").toString() == "Mood.SyncStates") { + emit gotMoodSync(message.value("params").toMap()); + m_manager->sendData(formatResponse(commandId)); + } + + if (message.value("method").toString() == "Tune.SyncStates") { + emit gotTuneSync(message.value("params").toMap()); + m_manager->sendData(formatResponse(commandId)); + } + +} diff --git a/plugins/deviceplugins/tune/jsonrpcserver.h b/plugins/deviceplugins/tune/jsonrpcserver.h new file mode 100644 index 00000000..54147508 --- /dev/null +++ b/plugins/deviceplugins/tune/jsonrpcserver.h @@ -0,0 +1,63 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This file is part of guh. * + * * + * Guh 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, version 2 of the License. * + * * + * Guh 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 for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with guh. If not, see . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef JSONRPCSERVER_H +#define JSONRPCSERVER_H + +#include +#include +#include + +#include "tunemanager.h" +#include "plugin/device.h" +#include "types/action.h" + +class JsonRpcServer : public QObject +{ + Q_OBJECT +public: + explicit JsonRpcServer(QObject *parent = 0); + + void start(); + void stop(); + + bool tuneAvailable(); + bool sync(QList deviceList); + void executeAction(Device* device, const Action &action); + +private: + TuneManager *m_manager; + int m_id; + + QHash m_requests; + + QByteArray formatResponse(int commandId, const QVariantMap &responseParams = QVariantMap()); + QByteArray formatErrorResponse(int commandId, const QString &errorMessage); + void handleResponse(const QVariantMap &response); + +signals: + void connectionStatusChanged(const bool &connectionStatus); + void gotTuneSync(const QVariantMap ¶ms); + void gotMoodSync(const QVariantMap ¶ms); + void gotActionResponse(const QVariantMap &response); + +private slots: + void processData(const QByteArray &data); +}; + +#endif // JSONRPCSERVER_H diff --git a/plugins/deviceplugins/tune/tune.pro b/plugins/deviceplugins/tune/tune.pro index 5e11d9a5..858ddf5c 100644 --- a/plugins/deviceplugins/tune/tune.pro +++ b/plugins/deviceplugins/tune/tune.pro @@ -4,10 +4,13 @@ TARGET = $$qtLibraryTarget(guh_deviceplugintune) SOURCES += \ deviceplugintune.cpp \ - tunemanager.cpp + tunemanager.cpp \ + jsonrpcserver.cpp HEADERS += \ deviceplugintune.h \ - tunemanager.h + tunemanager.h \ + jsonrpcserver.h + diff --git a/plugins/deviceplugins/tune/tunemanager.cpp b/plugins/deviceplugins/tune/tunemanager.cpp index aa3ae3b2..5869cd7d 100644 --- a/plugins/deviceplugins/tune/tunemanager.cpp +++ b/plugins/deviceplugins/tune/tunemanager.cpp @@ -76,7 +76,6 @@ void TuneManager::readData() QByteArray message; while (m_tune->canReadLine()) { QByteArray dataLine = m_tune->readLine(); - //qDebug() << " --> tune line in:" << dataLine; message.append(dataLine); if (dataLine.endsWith('\n')) { emit dataReady(message);