diff --git a/doc/images/kodi_remote.png b/doc/images/kodi_remote.png new file mode 100644 index 00000000..e6d8e842 Binary files /dev/null and b/doc/images/kodi_remote.png differ diff --git a/doc/images/kodi_services.png b/doc/images/kodi_services.png new file mode 100644 index 00000000..7e05ede2 Binary files /dev/null and b/doc/images/kodi_services.png differ diff --git a/doc/images/kodi_settings.png b/doc/images/kodi_settings.png new file mode 100644 index 00000000..37480345 Binary files /dev/null and b/doc/images/kodi_settings.png differ diff --git a/doc/images/kodi_upnp.png b/doc/images/kodi_upnp.png new file mode 100644 index 00000000..79fc0c67 Binary files /dev/null and b/doc/images/kodi_upnp.png differ diff --git a/plugins/deviceplugins/kodi/devicepluginkodi.cpp b/plugins/deviceplugins/kodi/devicepluginkodi.cpp index c8c95e87..e2592320 100644 --- a/plugins/deviceplugins/kodi/devicepluginkodi.cpp +++ b/plugins/deviceplugins/kodi/devicepluginkodi.cpp @@ -20,13 +20,34 @@ /*! \page kodi.html - \title Kodi + \title Kodi - Media Center \ingroup plugins \ingroup network - TODO: description + This plugin allowes you to controll the media center \l{http://kodi.tv/}{Kodi}. If you want to discover + and control Kodi with guh, you need to activate the remote access and the UPnP service. + \chapter "Activate UPnP" + In order to discover Kodi in the network, you need to activate the UPnP serive in the Kodi settings: + + \section2 Settings + \image kodi_settings.png + + \section2 Settings \unicode{0x2192} Services + \image kodi_services.png + + \section2 Settings \unicode{0x2192} Services \unicode{0x2192} UPnP + Activate all options. + \image kodi_upnp.png + + + \chapter Activate "Remote Control" + In order to control Kodi over the network with guh, you need to activate the remote control permissions: + + \section2 Settings \unicode{0x2192} Services \unicode{0x2192} Remote Control + Activate all options. + \image kodi_remote.png \chapter Plugin properties Following JSON file contains the definition and the description of all available \l{DeviceClass}{DeviceClasses} @@ -41,7 +62,7 @@ \note If a \l{StateType} has the parameter \tt{"writable": true}, an \l{ActionType} with the same uuid and \l{ParamType}{ParamTypes} will be created automatically. - \quotefile plugins/deviceplugins/udpcommander/devicepluginudpcommander.json + \quotefile plugins/deviceplugins/kodi/devicepluginkodi.json */ #include "devicepluginkodi.h" @@ -61,32 +82,38 @@ DeviceManager::HardwareResources DevicePluginKodi::requiredHardware() const DeviceManager::DeviceSetupStatus DevicePluginKodi::setupDevice(Device *device) { - KodiConnection *kodiConnection = new KodiConnection(QHostAddress(device->paramValue("ip").toString()), 9090, this); + Kodi *kodi= new Kodi(QHostAddress(device->paramValue("ip").toString()), 9090, this); - connect(kodiConnection, &KodiConnection::connectionStateChanged, this, &DevicePluginKodi::onConnectionChanged); + connect(kodi, &Kodi::connectionStatusChanged, this, &DevicePluginKodi::onConnectionChanged); + connect(kodi, &Kodi::stateChanged, this, &DevicePluginKodi::onStateChanged); + connect(kodi, &Kodi::actionExecuted, this, &DevicePluginKodi::onActionExecuted); + connect(kodi, &Kodi::onPlayerPlay, this, &DevicePluginKodi::onPlayerPlay); + connect(kodi, &Kodi::onPlayerPause, this, &DevicePluginKodi::onPlayerPause); + connect(kodi, &Kodi::onPlayerStop, this, &DevicePluginKodi::onPlayerStop); - kodiConnection->connectToKodi(); + kodi->connectKodi(); - m_kodiConnections.insert(kodiConnection, device); + m_kodis.insert(kodi, device); return DeviceManager::DeviceSetupStatusSuccess; } void DevicePluginKodi::deviceRemoved(Device *device) { - KodiConnection *kodiConnection = m_kodiConnections.key(device); - m_kodiConnections.remove(kodiConnection); - qCDebug(dcKodi) << "delete Kodi" << device->paramValue("name"); - kodiConnection->deleteLater(); + Kodi *kodi = m_kodis.key(device); + m_kodis.remove(kodi); + qCDebug(dcKodi) << "delete " << device->paramValue("name"); + kodi->deleteLater(); } void DevicePluginKodi::guhTimer() { - foreach (KodiConnection *kodi, m_kodiConnections.keys()) { + foreach (Kodi *kodi, m_kodis.keys()) { if (!kodi->connected()) { - kodi->connectToKodi(); + kodi->connectKodi(); continue; } else { - // update.. ? + // no need for polling information, notifications do the job + //kodi->update(); } } } @@ -96,7 +123,7 @@ DeviceManager::DeviceError DevicePluginKodi::discoverDevices(const DeviceClassId { Q_UNUSED(params) Q_UNUSED(deviceClassId) - qCDebug(dcKodi) << "start Kodi UPnP search"; + qCDebug(dcKodi) << "start UPnP search"; upnpDiscover(); return DeviceManager::DeviceErrorAsync; } @@ -134,31 +161,90 @@ void DevicePluginKodi::upnpDiscoveryFinished(const QList & DeviceManager::DeviceError DevicePluginKodi::executeAction(Device *device, const Action &action) { if (device->deviceClassId() == kodiDeviceClassId) { - if (action.actionTypeId() == sendNotificationActionTypeId) { + Kodi *kodi = m_kodis.key(device); + // check connection state + if (!kodi->connected()) { + return DeviceManager::DeviceErrorHardwareNotAvailable; + } - return DeviceManager::DeviceErrorNoError; + if (action.actionTypeId() == showNotificationActionTypeId) { + kodi->showNotification(action.param("message").value().toString(), 8000, action.id()); + return DeviceManager::DeviceErrorAsync; } else if (action.actionTypeId() == volumeActionTypeId) { - - - return DeviceManager::DeviceErrorNoError; + kodi->setVolume(action.param("volume").value().toInt(), action.id()); + return DeviceManager::DeviceErrorAsync; + } else if (action.actionTypeId() == muteActionTypeId) { + kodi->setMuted(action.param("mute").value().toBool(), action.id()); + return DeviceManager::DeviceErrorAsync; + } else if (action.actionTypeId() == pressButtonActionTypeId) { + kodi->pressButton(action.param("button").value().toString(), action.id()); + return DeviceManager::DeviceErrorAsync; + } else if (action.actionTypeId() == systemActionTypeId) { + kodi->systemCommand(action.param("command").value().toString(), action.id()); + return DeviceManager::DeviceErrorAsync; + } else if (action.actionTypeId() == videoLibraryActionTypeId) { + kodi->videoLibrary(action.param("command").value().toString(), action.id()); + return DeviceManager::DeviceErrorAsync; + } else if (action.actionTypeId() == audioLibraryActionTypeId) { + kodi->audioLibrary(action.param("command").value().toString(), action.id()); + return DeviceManager::DeviceErrorAsync; } return DeviceManager::DeviceErrorActionTypeNotFound; } return DeviceManager::DeviceErrorDeviceClassNotFound; } -void DevicePluginKodi::onConnectionChanged(const bool &connected) +void DevicePluginKodi::onConnectionChanged() { - KodiConnection *kodiConnection = static_cast(sender()); - Device *device = m_kodiConnections.value(kodiConnection); + Kodi *kodi = static_cast(sender()); + Device *device = m_kodis.value(kodi); - device->setStateValue(connectedStateTypeId, connected); + if (kodi->connected()) { + kodi->showNotification("Connected", 2000, ActionId()); + kodi->update(); + } + + device->setStateValue(connectedStateTypeId, kodi->connected()); } -void DevicePluginKodi::dataReceived(const QByteArray &data) +void DevicePluginKodi::onStateChanged() { - KodiConnection *kodiConnection = static_cast(sender()); - emit dataReady(kodiConnection, data); + Kodi *kodi = static_cast(sender()); + Device *device = m_kodis.value(kodi); + + // set device state values + device->setStateValue(volumeStateTypeId, kodi->volume()); + device->setStateValue(muteStateTypeId, kodi->muted()); +} + +void DevicePluginKodi::onActionExecuted(const ActionId &actionId, const bool &success) +{ + if (success) { + emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorNoError); + } else { + emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorInvalidParameter); + } +} + +void DevicePluginKodi::onPlayerPlay() +{ + Kodi *kodi = static_cast(sender()); + Device *device = m_kodis.value(kodi); + emit emitEvent(Event(onPlayerPlayEventTypeId, device->id())); +} + +void DevicePluginKodi::onPlayerPause() +{ + Kodi *kodi = static_cast(sender()); + Device *device = m_kodis.value(kodi); + emit emitEvent(Event(onPlayerPauseEventTypeId, device->id())); +} + +void DevicePluginKodi::onPlayerStop() +{ + Kodi *kodi = static_cast(sender()); + Device *device = m_kodis.value(kodi); + emit emitEvent(Event(onPlayerStopEventTypeId, device->id())); } diff --git a/plugins/deviceplugins/kodi/devicepluginkodi.h b/plugins/deviceplugins/kodi/devicepluginkodi.h index cf5ff6aa..a9c59173 100644 --- a/plugins/deviceplugins/kodi/devicepluginkodi.h +++ b/plugins/deviceplugins/kodi/devicepluginkodi.h @@ -22,7 +22,7 @@ #define DEVICEPLUGINKODI_H #include "plugin/deviceplugin.h" -#include "kodiconnection.h" +#include "kodi.h" #include #include @@ -48,15 +48,16 @@ public: DeviceManager::DeviceError executeAction(Device *device, const Action &action) override; private: - QHash m_kodiConnections; - -signals: - void dataReady(KodiConnection *kodiConnection, const QByteArray &data); + QHash m_kodis; private slots: - void onConnectionChanged(const bool &connected); - void dataReceived(const QByteArray &data); + void onConnectionChanged(); + void onStateChanged(); + void onActionExecuted(const ActionId &actionId, const bool &success); + void onPlayerPlay(); + void onPlayerPause(); + void onPlayerStop(); }; #endif // DEVICEPLUGINKODI_H diff --git a/plugins/deviceplugins/kodi/devicepluginkodi.json b/plugins/deviceplugins/kodi/devicepluginkodi.json index d89bdafc..efbdc717 100644 --- a/plugins/deviceplugins/kodi/devicepluginkodi.json +++ b/plugins/deviceplugins/kodi/devicepluginkodi.json @@ -53,15 +53,123 @@ } } ], + "eventTypes": [ + { + "id": "2535a1eb-7643-4874-98f6-b027fdff6311", + "idName": "onPlayerPlay", + "name": "player play" + }, + { + "id": "99498b1c-e9c0-480a-9e91-662ee79ba976", + "idName": "onPlayerPause", + "name": "player pause" + }, + { + "id": "a02ce255-3abb-435d-a92e-7f99c952ecb2", + "idName": "onPlayerStop", + "name": "player stop" + } + ], "actionTypes": [ { "id": "dc0aa3b5-4eae-4e58-a4ac-d4c124da53f1", - "idName": "sendNotification", - "name": "send notification", + "idName": "showNotification", + "name": "show notification", "paramTypes": [ { "name": "message", - "type": "QString" + "type": "QString", + "inputType": "TextLine" + } + ] + }, + { + "id": "28060803-aa85-44a4-9dec-ee669dfb629f", + "idName": "pressButton", + "name": "press button", + "paramTypes": [ + { + "name": "button", + "type": "QString", + "allowedValues": [ + "left", + "right", + "up", + "down", + "pageup", + "pagedown", + "select", + "back", + "info", + "pause", + "stop", + "skipnext", + "skipprevious", + "stepforward", + "stepback", + "osd", + "play", + "playpause", + "fastforward", + "rewind", + "togglefullscreen", + "mute", + "volumeup", + "volumedown", + "channelup", + "channeldown", + "red", + "green", + "yellow", + "blue" + ] + } + ] + }, + { + "id": "8d51cd64-ecef-44f8-85b0-697bef252ce5", + "idName": "system", + "name": "system", + "paramTypes": [ + { + "name": "command", + "type": "QString", + "allowedValues": [ + "hibernate", + "reboot", + "shutdown", + "suspend" + ] + } + ] + }, + { + "id": "59f7ad10-16eb-40b7-a88b-c8393ae8e413", + "idName": "videoLibrary", + "name": "video library", + "paramTypes": [ + { + "name": "command", + "type": "QString", + "allowedValues": [ + "scan", + "clean" + ] + } + ] + }, + { + "id": "3d5120eb-1007-46c3-b76e-d9d4b105b9f2", + "idName": "audioLibrary", + "name": "audio library", + "paramTypes": [ + { + "name": "command", + "type": "QString", + "allowedValues": [ + "scan", + "clean" + ] } ] } diff --git a/plugins/deviceplugins/kodi/jsonhandler.cpp b/plugins/deviceplugins/kodi/jsonhandler.cpp index 942f2686..7558dfbd 100644 --- a/plugins/deviceplugins/kodi/jsonhandler.cpp +++ b/plugins/deviceplugins/kodi/jsonhandler.cpp @@ -1,42 +1,133 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2015 Simon Stuerz * + * * + * 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 "jsonhandler.h" #include "loggingcategories.h" -#include -#include +#include -JsonHandler::JsonHandler(QObject *parent) : - QObject(parent) +JsonHandler::JsonHandler(KodiConnection *connection, QObject *parent) : + QObject(parent), + m_connection(connection), + m_id(0) { + connect(m_connection, &KodiConnection::dataReady, this, &JsonHandler::processResponse); } -//QByteArray JsonHandler::createHelloMessage(QString title, QString message) -//{ -// QByteArray payload; +void JsonHandler::sendData(const QString &method, const QVariantMap ¶ms, const ActionId &actionId) +{ + QVariantMap package; + package.insert("id", m_id); + package.insert("method", method); + package.insert("params", params); + package.insert("jsonrpc", "2.0"); -// QByteArray iconData; -// QBuffer buffer(&iconData); -// buffer.open(QIODevice::WriteOnly); -// icon.save(&buffer, "PNG"); -// // payload.clear(); + m_replys.insert(m_id, KodiReply(method, params, actionId)); -// // // titel -// // payload.push_back(QByteArray::fromStdString(title.toStdString())); -// // payload.push_back('\0'); + QJsonDocument jsonDoc = QJsonDocument::fromVariant(package); + qCDebug(dcKodi) << "sending data" << jsonDoc.toJson(); + m_connection->sendData(jsonDoc.toJson()); + m_id++; +} -// // // message -// // payload.push_back(QByteArray::fromStdString(message.toStdString())); -// // payload.push_back('\0'); +void JsonHandler::processNotification(const QString &method, const QVariantMap ¶ms) +{ + if (method == "Application.OnVolumeChanged") { + QVariantMap data = params.value("data").toMap(); + qCDebug(dcKodi) << "got volume changed notification" << "volume:" << data.value("volume").toInt() << " muted:" << data.value("muted").toBool(); + emit volumeChanged(data.value("volume").toInt(), data.value("muted").toBool()); + } else if (method == "Player.onPlayerPlay") { + qCDebug(dcKodi) << "got player play notification"; + emit onPlayerPlay(); + } else if (method == "Player.onPlayerPause") { + qCDebug(dcKodi) << "got player pause notification"; + emit onPlayerPause(); + } else if (method == "Player.onPlayerStop") { + qCDebug(dcKodi) << "got player stop notification"; + emit onPlayerPause(); + } -// // // icontype ( 0=>NOICON, 1=>JPEG , 2=>PNG , 3=>GIF ) -// // payload.push_back(2); +} -// // payload.push_back("0000"); +void JsonHandler::processActionResponse(const KodiReply &reply, const QVariantMap &response) +{ + if (response.contains("error")) { + qCDebug(dcKodi) << QJsonDocument::fromVariant(response).toJson(); + qCWarning(dcKodi) << "got action error response:" << response.value("error").toMap().value("message").toString(); + emit actionExecuted(reply.actionId(), false); + } else { + emit actionExecuted(reply.actionId(), true); + } +} -// // // image data -// // payload.push_back(iconData); -// // payload.push_back('\0'); +void JsonHandler::processRequestResponse(const KodiReply &reply, const QVariantMap &response) +{ + if (response.contains("error")) { + qCDebug(dcKodi) << QJsonDocument::fromVariant(response).toJson(); + qCWarning(dcKodi) << "got request error response:" << response.value("error").toMap().value("message").toString(); + } -// // qCDebug(dcKodi) << payload; + if (reply.method() == "Application.GetProperties") { + qCDebug(dcKodi) << "got update response" << response; + emit updateDataReceived(response.value("result").toMap()); + } +} -// return payload; -//} +void JsonHandler::processResponse(const QByteArray &data) +{ + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); + + if(error.error != QJsonParseError::NoError) { + qCWarning(dcKodi) << "failed to parse JSON data:" << data << ":" << error.errorString(); + return; + } + qCDebug(dcKodi) << "data received:" << jsonDoc.toJson(); + + QVariantMap message = jsonDoc.toVariant().toMap(); + + // check jsonrpc value + if (!message.contains("jsonrpc") || message.value("jsonrpc").toString() != "2.0") { + qCWarning(dcKodi) << "jsonrpc 2.0 value missing in message" << data; + } + + // check id (if there is no id, it's an notification from kodi) + if (!message.contains("id")) { + + // check method + if (!message.contains("method")) { + qCWarning(dcKodi) << "method missing in message" << data; + } + + processNotification(message.value("method").toString(), message.value("params").toMap()); + return; + } + + int id = message.value("id").toInt(); + KodiReply reply = m_replys.take(id); + + // check if this message is a response to an action call + if (reply.actionId() != ActionId()) { + processActionResponse(reply, message); + return; + } + + processRequestResponse(reply, message); +} diff --git a/plugins/deviceplugins/kodi/jsonhandler.h b/plugins/deviceplugins/kodi/jsonhandler.h index 618e1c1e..14b1c4c2 100644 --- a/plugins/deviceplugins/kodi/jsonhandler.h +++ b/plugins/deviceplugins/kodi/jsonhandler.h @@ -1,19 +1,63 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2015 Simon Stuerz * + * * + * 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 JSONHANDLER_H #define JSONHANDLER_H #include +#include +#include + +#include "kodiconnection.h" +#include "kodireply.h" +#include "typeutils.h" class JsonHandler : public QObject { Q_OBJECT public: - explicit JsonHandler(QObject *parent = 0); + explicit JsonHandler(KodiConnection *connection = 0, QObject *parent = 0); - //static QByteArray createHelloMessage(QString title, QString message); + void sendData(const QString &method, const QVariantMap ¶ms, const ActionId &actionId); + +private: + KodiConnection *m_connection; + int m_id; + + QHash m_replys; + + void processNotification(const QString &method, const QVariantMap ¶ms); + void processActionResponse(const KodiReply &reply, const QVariantMap &response); + void processRequestResponse(const KodiReply &reply, const QVariantMap &response); signals: + void volumeChanged(const int &volume, const bool &muted); + void actionExecuted(const ActionId &actionId, const bool &success); + void updateDataReceived(const QVariantMap &data); -public slots: + void onPlayerPlay(); + void onPlayerPause(); + void onPlayerStop(); + +private slots: + void processResponse(const QByteArray &data); }; diff --git a/plugins/deviceplugins/kodi/kodi.cpp b/plugins/deviceplugins/kodi/kodi.cpp new file mode 100644 index 00000000..621660d6 --- /dev/null +++ b/plugins/deviceplugins/kodi/kodi.cpp @@ -0,0 +1,182 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2015 Simon Stuerz * + * * + * 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 "kodi.h" + +Kodi::Kodi(const QHostAddress &hostAddress, const int &port, QObject *parent) : + QObject(parent) +{ + m_connection = new KodiConnection(hostAddress, port, this); + connect (m_connection, &KodiConnection::connectionStatusChanged, this, &Kodi::connectionStatusChanged); + + m_jsonHandler = new JsonHandler(m_connection, this); + connect(m_jsonHandler, &JsonHandler::volumeChanged, this, &Kodi::onVolumeChanged); + connect(m_jsonHandler, &JsonHandler::actionExecuted, this, &Kodi::actionExecuted); + connect(m_jsonHandler, &JsonHandler::updateDataReceived, this, &Kodi::onUpdateFinished); + connect(m_jsonHandler, &JsonHandler::onPlayerPlay, this, &Kodi::onPlayerPlay); + connect(m_jsonHandler, &JsonHandler::onPlayerPause, this, &Kodi::onPlayerPause); + connect(m_jsonHandler, &JsonHandler::onPlayerStop, this, &Kodi::onPlayerStop); +} + +QHostAddress Kodi::hostAddress() const +{ + return m_connection->hostAddress(); +} + +int Kodi::port() const +{ + return m_connection->port(); +} + +bool Kodi::connected() const +{ + return m_connection->connected(); +} + +void Kodi::setMuted(const bool &muted, const ActionId &actionId) +{ + QVariantMap params; + params.insert("mute", muted); + + m_jsonHandler->sendData("Application.SetMute", params, actionId); +} + +bool Kodi::muted() const +{ + return m_muted; +} + +void Kodi::setVolume(const int &volume, const ActionId &actionId) +{ + QVariantMap params; + params.insert("volume", volume); + + m_jsonHandler->sendData("Application.SetVolume", params, actionId); +} + +int Kodi::volume() const +{ + return m_volume; +} + +void Kodi::showNotification(const QString &message, const int &displayTime, const ActionId &actionId) +{ + QVariantMap params; + params.insert("title", "guh notification"); + params.insert("message", message); + params.insert("displaytime", displayTime); + m_jsonHandler->sendData("GUI.ShowNotification", params, actionId); +} + +void Kodi::pressButton(const QString &button, const ActionId &actionId) +{ + QVariantMap params; + params.insert("action", button); + m_jsonHandler->sendData("Input.ExecuteAction", params, actionId); +} + +void Kodi::systemCommand(const QString &command, const ActionId &actionId) +{ + QString method; + if (command == "hibernate") { + method = "Hibernate"; + } else if (command == "reboot") { + method = "Reboot"; + } else if (command == "shutdown") { + method = "Shutdown"; + } else if (command == "suspend") { + method = "Suspend"; + } else { + // already checkt with allowed values + } + + m_jsonHandler->sendData("System." + method, QVariantMap(), actionId); +} + +void Kodi::videoLibrary(const QString &command, const ActionId &actionId) +{ + QString method; + if (command == "scan") { + method = "Scan"; + } else if (command == "clean") { + method = "Clean"; + } else { + // already checkt with allowed values + } + + m_jsonHandler->sendData("VideoLibrary." + method, QVariantMap(), actionId); +} + +void Kodi::audioLibrary(const QString &command, const ActionId &actionId) +{ + QString method; + if (command == "scan") { + method = "Scan"; + } else if (command == "clean") { + method = "Clean"; + } else { + // already checkt with allowed values + } + + m_jsonHandler->sendData("AudioLibrary." + method, QVariantMap(), actionId); +} + +void Kodi::update() +{ + QVariantMap params; + QVariantList properties; + properties.append("volume"); + properties.append("muted"); + properties.append("name"); + properties.append("version"); + params.insert("properties", properties); + + m_jsonHandler->sendData("Application.GetProperties", params, ActionId()); +} + +void Kodi::connectKodi() +{ + m_connection->connectKodi(); +} + +void Kodi::disconnectKodi() +{ + m_connection->disconnectKodi(); +} + +void Kodi::onVolumeChanged(const int &volume, const bool &muted) +{ + if (m_volume != volume || m_muted != muted) { + m_volume = volume; + m_muted = muted; + emit stateChanged(); + } +} + +void Kodi::onUpdateFinished(const QVariantMap &data) +{ + if (data.contains("volume")) { + m_volume = data.value("volume").toInt(); + } + if (data.contains("muted")) { + m_volume = data.value("muted").toBool(); + } + emit stateChanged(); +} diff --git a/plugins/deviceplugins/kodi/kodi.h b/plugins/deviceplugins/kodi/kodi.h new file mode 100644 index 00000000..c6d1360c --- /dev/null +++ b/plugins/deviceplugins/kodi/kodi.h @@ -0,0 +1,83 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2015 Simon Stuerz * + * * + * 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 KODI_H +#define KODI_H + +#include +#include + +#include "kodiconnection.h" +#include "jsonhandler.h" + +class Kodi : public QObject +{ + Q_OBJECT +public: + + explicit Kodi(const QHostAddress &hostAddress, const int &port = 9090, QObject *parent = 0); + + QHostAddress hostAddress() const; + int port() const; + + bool connected() const; + + // propertys + void setMuted(const bool &muted, const ActionId &actionId); + bool muted() const; + + void setVolume(const int &volume, const ActionId &actionId); + int volume() const; + + // actions + void showNotification(const QString &message, const int &displayTime, const ActionId &actionId); + void pressButton(const QString &button, const ActionId &actionId); + void systemCommand(const QString &command, const ActionId &actionId); + void videoLibrary(const QString &command, const ActionId &actionId); + void audioLibrary(const QString &command, const ActionId &actionId); + + void update(); + + void connectKodi(); + void disconnectKodi(); + +private: + KodiConnection *m_connection; + JsonHandler *m_jsonHandler; + + bool m_muted; + int m_volume; + +signals: + void connectionStatusChanged(); + void stateChanged(); + void actionExecuted(const ActionId &actionId, const bool &success); + void onPlayerPlay(); + void onPlayerPause(); + void onPlayerStop(); + +private slots: + void onVolumeChanged(const int &volume, const bool &muted); + void onUpdateFinished(const QVariantMap &data); + + +}; + +#endif // KODI_H diff --git a/plugins/deviceplugins/kodi/kodi.pro b/plugins/deviceplugins/kodi/kodi.pro index e0accf03..fd1c2622 100644 --- a/plugins/deviceplugins/kodi/kodi.pro +++ b/plugins/deviceplugins/kodi/kodi.pro @@ -7,10 +7,14 @@ RESOURCES += images.qrc \ SOURCES += \ devicepluginkodi.cpp \ kodiconnection.cpp \ - jsonhandler.cpp + jsonhandler.cpp \ + kodi.cpp \ + kodireply.cpp HEADERS += \ devicepluginkodi.h \ kodiconnection.h \ - jsonhandler.h + jsonhandler.h \ + kodi.h \ + kodireply.h diff --git a/plugins/deviceplugins/kodi/kodiconnection.cpp b/plugins/deviceplugins/kodi/kodiconnection.cpp index bc08698e..95adf2a2 100644 --- a/plugins/deviceplugins/kodi/kodiconnection.cpp +++ b/plugins/deviceplugins/kodi/kodiconnection.cpp @@ -1,3 +1,23 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2015 Simon Stuerz * + * * + * 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 "kodiconnection.h" #include "loggingcategories.h" #include "jsonhandler.h" @@ -18,7 +38,7 @@ KodiConnection::KodiConnection(const QHostAddress &hostAddress, const int &port, connect(m_socket, &QTcpSocket::readyRead, this, &KodiConnection::readData); } -void KodiConnection::connectToKodi() +void KodiConnection::connectKodi() { if (m_socket->state() == QAbstractSocket::ConnectingState) { return; @@ -26,7 +46,7 @@ void KodiConnection::connectToKodi() m_socket->connectToHost(m_hostAddress, m_port); } -void KodiConnection::disconnectFromKodi() +void KodiConnection::disconnectKodi() { m_socket->close(); } @@ -50,51 +70,31 @@ void KodiConnection::onConnected() { qCDebug(dcKodi) << "connected successfully to" << hostAddress().toString() << port(); m_connected = true; -// QPixmap logo = QPixmap(":/images/guh-logo.png"); -// qCDebug(dcKodi) << "image size" << logo.size(); - emit connectionStateChanged(true); + emit connectionStatusChanged(); } void KodiConnection::onDisconnected() { qCDebug(dcKodi) << "disconnected from" << hostAddress().toString() << port(); m_connected = false; - emit connectionStateChanged(false); + emit connectionStatusChanged(); } void KodiConnection::onError(QAbstractSocket::SocketError socketError) { - qCWarning(dcKodi) << "socket error:" << socketError << m_socket->errorString(); + if (connected()) { + qCWarning(dcKodi) << "socket error:" << socketError << m_socket->errorString(); + } } void KodiConnection::readData() { QByteArray data = m_socket->readAll(); - - QJsonParseError error; - QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); - - if(error.error != QJsonParseError::NoError) { - qCWarning(dcKodi) << "failed to parse JSON data:" << data << ":" << error.errorString(); - return; - } - qCDebug(dcKodi) << "data received:" << jsonDoc.toJson(); - emit dataReady(data); } -void KodiConnection::sendData(const QString &method, const QVariantMap ¶ms) +void KodiConnection::sendData(const QByteArray &message) { - QVariantMap package; - package.insert("id", m_id); - package.insert("method", method); - package.insert("params", params); - package.insert("jsonrpc", "2.0"); - m_id++; - - QJsonDocument jsonDoc = QJsonDocument::fromVariant(package); - qCDebug(dcKodi) << "sending data" << jsonDoc.toJson(); - - m_socket->write(jsonDoc.toJson()); + m_socket->write(message); } diff --git a/plugins/deviceplugins/kodi/kodiconnection.h b/plugins/deviceplugins/kodi/kodiconnection.h index e7b08c21..b4eb89f1 100644 --- a/plugins/deviceplugins/kodi/kodiconnection.h +++ b/plugins/deviceplugins/kodi/kodiconnection.h @@ -1,3 +1,23 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2015 Simon Stuerz * + * * + * 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 KODICONNECTION_H #define KODICONNECTION_H @@ -12,8 +32,8 @@ class KodiConnection : public QObject public: explicit KodiConnection(const QHostAddress &hostAddress, const int &port = 9090, QObject *parent = 0); - void connectToKodi(); - void disconnectFromKodi(); + void connectKodi(); + void disconnectKodi(); QHostAddress hostAddress() const; int port() const; @@ -21,14 +41,13 @@ public: bool connected(); private: + QTcpSocket *m_socket; + QHostAddress m_hostAddress; int m_port; - int m_id; bool m_connected; - QTcpSocket *m_socket; - private slots: void onConnected(); void onDisconnected(); @@ -36,11 +55,11 @@ private slots: void readData(); signals: - void connectionStateChanged(const bool &connected); + void connectionStatusChanged(); void dataReady(const QByteArray &data); public slots: - void sendData(const QString &method, const QVariantMap ¶ms = QVariantMap()); + void sendData(const QByteArray &message); }; diff --git a/plugins/deviceplugins/kodi/kodireply.cpp b/plugins/deviceplugins/kodi/kodireply.cpp new file mode 100644 index 00000000..9cbef936 --- /dev/null +++ b/plugins/deviceplugins/kodi/kodireply.cpp @@ -0,0 +1,62 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2015 Simon Stuerz * + * * + * 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 "kodireply.h" + +KodiReply::KodiReply() +{ +} + +KodiReply::KodiReply(const QString &method, const QVariantMap ¶ms, const ActionId &actionId) : + m_method(method), + m_params(params), + m_actionId(actionId) +{ +} + +void KodiReply::setActionId(const ActionId &actionId) +{ + m_actionId = actionId; +} + +ActionId KodiReply::actionId() const +{ + return m_actionId; +} + +void KodiReply::setMethod(const QString &method) +{ + m_method = method; +} + +QString KodiReply::method() const +{ + return m_method; +} + +void KodiReply::setParams(const QVariantMap ¶ms) +{ + m_params = params; +} + +QVariantMap KodiReply::params() const +{ + return m_params; +} diff --git a/plugins/deviceplugins/kodi/kodireply.h b/plugins/deviceplugins/kodi/kodireply.h new file mode 100644 index 00000000..948c3c2f --- /dev/null +++ b/plugins/deviceplugins/kodi/kodireply.h @@ -0,0 +1,51 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2015 Simon Stuerz * + * * + * 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 KODIREPLY_H +#define KODIREPLY_H + +#include +#include + +#include "typeutils.h" + +class KodiReply +{ +public: + KodiReply(); + KodiReply(const QString &method, const QVariantMap ¶ms, const ActionId &actionId = ActionId()); + + void setActionId(const ActionId &actionId); + ActionId actionId() const; + + void setMethod(const QString &method); + QString method() const; + + void setParams(const QVariantMap ¶ms); + QVariantMap params() const; + +private: + QString m_method; + QVariantMap m_params; + ActionId m_actionId; + +}; + +#endif // KODIREPLY_H