diff --git a/debian/guh-plugins.install b/debian/guh-plugins.install index b8576439..753c713a 100644 --- a/debian/guh-plugins.install +++ b/debian/guh-plugins.install @@ -17,3 +17,5 @@ usr/lib/guh/plugins/libguh_devicepluginunitec.so usr/lib/guh/plugins/libguh_devicepluginleynew.so usr/lib/guh/plugins/libguh_deviceplugintune.so usr/lib/guh/plugins/libguh_devicepluginudpcommander.so +usr/lib/guh/plugins/libguh_devicepluginkodi.so + diff --git a/libguh/loggingcategories.cpp b/libguh/loggingcategories.cpp index 59d9550b..d5956fab 100644 --- a/libguh/loggingcategories.cpp +++ b/libguh/loggingcategories.cpp @@ -48,3 +48,4 @@ Q_LOGGING_CATEGORY(dcUdpCommander, "UdpCommander") Q_LOGGING_CATEGORY(dcWakeOnLan, "WakeOnLan") Q_LOGGING_CATEGORY(dcWemo, "Wemo") Q_LOGGING_CATEGORY(dcWifiDetector, "WifiDetector") +Q_LOGGING_CATEGORY(dcKodi, "Kodi") diff --git a/libguh/loggingcategories.h b/libguh/loggingcategories.h index 41d55a91..0b7e848c 100644 --- a/libguh/loggingcategories.h +++ b/libguh/loggingcategories.h @@ -53,6 +53,7 @@ Q_DECLARE_LOGGING_CATEGORY(dcUdpCommander) Q_DECLARE_LOGGING_CATEGORY(dcWakeOnLan) Q_DECLARE_LOGGING_CATEGORY(dcWemo) Q_DECLARE_LOGGING_CATEGORY(dcWifiDetector) +Q_DECLARE_LOGGING_CATEGORY(dcKodi) diff --git a/plugins/deviceplugins/kodi/devicepluginkodi.cpp b/plugins/deviceplugins/kodi/devicepluginkodi.cpp index 7f975e6d..50d70cc0 100644 --- a/plugins/deviceplugins/kodi/devicepluginkodi.cpp +++ b/plugins/deviceplugins/kodi/devicepluginkodi.cpp @@ -25,6 +25,8 @@ \ingroup plugins \ingroup network + TODO: description + \chapter Plugin properties Following JSON file contains the definition and the description of all available \l{DeviceClass}{DeviceClasses} @@ -45,6 +47,7 @@ #include "devicepluginkodi.h" #include "plugin/device.h" #include "plugininfo.h" +#include "loggingcategories.h" DevicePluginKodi::DevicePluginKodi() { @@ -53,52 +56,85 @@ DevicePluginKodi::DevicePluginKodi() DeviceManager::HardwareResources DevicePluginKodi::requiredHardware() const { - return DeviceManager::HardwareResourceNone; + return DeviceManager::HardwareResourceTimer | DeviceManager::HardwareResourceUpnpDisovery; } DeviceManager::DeviceSetupStatus DevicePluginKodi::setupDevice(Device *device) { - Q_UNUSED(device) + KodiConnection *kodiConnection = new KodiConnection(QHostAddress(device->paramValue("ip").toString()), 9090, this); + connect(kodiConnection, &KodiConnection::connectionStateChanged, this, &DevicePluginKodi::onConnectionChanged); + + kodiConnection->connectToKodi(); + + m_kodiConnections.insert(kodiConnection, device); return DeviceManager::DeviceSetupStatusSuccess; } void DevicePluginKodi::deviceRemoved(Device *device) { - Q_UNUSED(device) + KodiConnection *kodiConnection = m_kodiConnections.key(device); + m_kodiConnections.remove(kodiConnection); + qCDebug(dcKodi) << "delete Kodi" << device->paramValue("name"); + kodiConnection->deleteLater(); } -DeviceManager::DeviceSetupStatus DevicePluginKodi::confirmPairing(const PairingTransactionId &pairingTransactionId, const DeviceClassId &deviceClassId, const ParamList ¶ms) -{ - Q_UNUSED(pairingTransactionId) - Q_UNUSED(deviceClassId) - Q_UNUSED(params) - - return DeviceManager::DeviceSetupStatusSuccess; -} DeviceManager::DeviceError DevicePluginKodi::discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) { Q_UNUSED(params) Q_UNUSED(deviceClassId) - + qCDebug(dcKodi) << "start Kodi UPnP search"; upnpDiscover(); - - return DeviceManager::DeviceErrorNoError; + return DeviceManager::DeviceErrorAsync; } void DevicePluginKodi::upnpDiscoveryFinished(const QList &upnpDeviceDescriptorList) { - Q_UNUSED(upnpDeviceDescriptorList) - + QList deviceDescriptors; + foreach (const UpnpDeviceDescriptor &upnpDescriptor, upnpDeviceDescriptorList) { + if (upnpDescriptor.modelName().contains("Kodi")) { + qCDebug(dcKodi) << upnpDescriptor; + DeviceDescriptor deviceDescriptor(kodiDeviceClassId, "Kodi - Media Center", upnpDescriptor.hostAddress().toString()); + ParamList params; + params.append(Param("name", upnpDescriptor.friendlyName())); + params.append(Param("ip", upnpDescriptor.hostAddress().toString())); + params.append(Param("port", 9090)); + deviceDescriptor.setParams(params); + deviceDescriptors.append(deviceDescriptor); + } + } + emit devicesDiscovered(kodiDeviceClassId, deviceDescriptors); } -void DevicePluginKodi::networkManagerReplyReady(QNetworkReply *reply) +DeviceManager::DeviceError DevicePluginKodi::executeAction(Device *device, const Action &action) { - Q_UNUSED(reply) + if (device->deviceClassId() == kodiDeviceClassId) { + if (action.actionTypeId() == sendNotificationActionTypeId) { + + + return DeviceManager::DeviceErrorNoError; + } else if (action.actionTypeId() == volumeActionTypeId) { + + + return DeviceManager::DeviceErrorNoError; + } + return DeviceManager::DeviceErrorActionTypeNotFound; + } + return DeviceManager::DeviceErrorDeviceClassNotFound; } -void DevicePluginKodi::guhTimer() +void DevicePluginKodi::onConnectionChanged(const bool &connected) { + KodiConnection *kodiConnection = static_cast(sender()); + Device *device = m_kodiConnections.value(kodiConnection); + device->setStateValue(connectedStateTypeId, connected); } + +void DevicePluginKodi::dataReceived(const QByteArray &data) +{ + KodiConnection *kodiConnection = static_cast(sender()); + emit dataReady(kodiConnection, data); +} + diff --git a/plugins/deviceplugins/kodi/devicepluginkodi.h b/plugins/deviceplugins/kodi/devicepluginkodi.h index 1801f924..63d551a1 100644 --- a/plugins/deviceplugins/kodi/devicepluginkodi.h +++ b/plugins/deviceplugins/kodi/devicepluginkodi.h @@ -22,9 +22,11 @@ #define DEVICEPLUGINKODI_H #include "plugin/deviceplugin.h" +#include "kodiconnection.h" #include #include +#include class DevicePluginKodi : public DevicePlugin { @@ -39,16 +41,20 @@ public: DeviceManager::DeviceSetupStatus setupDevice(Device *device) override; void deviceRemoved(Device *device) override; - DeviceManager::DeviceSetupStatus confirmPairing(const PairingTransactionId &pairingTransactionId, const DeviceClassId &deviceClassId, const ParamList ¶ms) override; - DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override; void upnpDiscoveryFinished(const QList &upnpDeviceDescriptorList) override; - void networkManagerReplyReady(QNetworkReply *reply) override; - void guhTimer() override; + + DeviceManager::DeviceError executeAction(Device *device, const Action &action) override; private: + QHash m_kodiConnections; + +signals: + void dataReady(KodiConnection *kodiConnection, const QByteArray &data); private slots: + void onConnectionChanged(const bool &connected); + void dataReceived(const QByteArray &data); }; diff --git a/plugins/deviceplugins/kodi/devicepluginkodi.json b/plugins/deviceplugins/kodi/devicepluginkodi.json index 2d368b96..8ed737d0 100644 --- a/plugins/deviceplugins/kodi/devicepluginkodi.json +++ b/plugins/deviceplugins/kodi/devicepluginkodi.json @@ -8,13 +8,60 @@ "deviceClasses": [ { "deviceClassId": "d09953e3-c5bd-415b-973b-0d0bf2be3f69", + "idName": "kodi", "name": "Kodi", - "createMethods": ["discovery"], + "createMethods": ["user", "discovery"], "paramTypes": [ { "name": "name", "type": "QString", "inputType": "TextLine" + }, + { + "name": "ip", + "type" : "QString", + "inputType": "IPv4Address" + }, + { + "name": "port", + "type" : "int" + } + ], + "stateTypes": [ + { + "id": "09dfbd40-c97c-4a20-9ecd-f80e389a4864", + "idName": "connected", + "name": "connected", + "type": "bool" + }, + { + "id": "bc98cdb0-4d0e-48ca-afc7-922e49bb7813", + "idName": "mute", + "name": "mute", + "type": "bool" + }, + { + "id": "9dfe5d78-4c3f-497c-bab1-bb9fdf7e93a9", + "idName": "volume", + "name": "volume", + "unit": "Percentage", + "type": "int", + "writable": true, + "minValue": 0, + "maxValue": 100 + } + ], + "actionTypes": [ + { + "id": "dc0aa3b5-4eae-4e58-a4ac-d4c124da53f1", + "idName": "sendNotification", + "name": "send notification", + "paramTypes": [ + { + "name": "message", + "type": "QString" + } + ] } ] } diff --git a/plugins/deviceplugins/kodi/jsonhandler.cpp b/plugins/deviceplugins/kodi/jsonhandler.cpp new file mode 100644 index 00000000..f0a320df --- /dev/null +++ b/plugins/deviceplugins/kodi/jsonhandler.cpp @@ -0,0 +1,6 @@ +#include "jsonhandler.h" + +JsonHandler::JsonHandler(QObject *parent) : + QObject(parent) +{ +} diff --git a/plugins/deviceplugins/kodi/jsonhandler.h b/plugins/deviceplugins/kodi/jsonhandler.h new file mode 100644 index 00000000..005ceaec --- /dev/null +++ b/plugins/deviceplugins/kodi/jsonhandler.h @@ -0,0 +1,18 @@ +#ifndef JSONHANDLER_H +#define JSONHANDLER_H + +#include + +class JsonHandler : public QObject +{ + Q_OBJECT +public: + explicit JsonHandler(QObject *parent = 0); + +signals: + +public slots: + +}; + +#endif // JSONHANDLER_H diff --git a/plugins/deviceplugins/kodi/kodi.pro b/plugins/deviceplugins/kodi/kodi.pro index 152238cd..b7fca995 100644 --- a/plugins/deviceplugins/kodi/kodi.pro +++ b/plugins/deviceplugins/kodi/kodi.pro @@ -3,8 +3,12 @@ include(../../plugins.pri) TARGET = $$qtLibraryTarget(guh_devicepluginkodi) SOURCES += \ - devicepluginkodi.cpp + devicepluginkodi.cpp \ + kodiconnection.cpp \ + jsonhandler.cpp HEADERS += \ - devicepluginkodi.h + devicepluginkodi.h \ + kodiconnection.h \ + jsonhandler.h diff --git a/plugins/deviceplugins/kodi/kodiconnection.cpp b/plugins/deviceplugins/kodi/kodiconnection.cpp new file mode 100644 index 00000000..e6aa835c --- /dev/null +++ b/plugins/deviceplugins/kodi/kodiconnection.cpp @@ -0,0 +1,85 @@ +#include "kodiconnection.h" +#include "loggingcategories.h" + +KodiConnection::KodiConnection(const QHostAddress &hostAddress, const int &port, QObject *parent) : + QObject(parent), + m_hostAddress(hostAddress), + m_port(port) +{ + m_socket = new QTcpSocket(this); + + connect(m_socket, &QTcpSocket::connected, this, &KodiConnection::onConnected); + connect(m_socket, &QTcpSocket::disconnected, this, &KodiConnection::onDisconnected); + connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onError(QAbstractSocket::SocketError))); + connect(m_socket, &QTcpSocket::readyRead, this, &KodiConnection::readData); +} + +void KodiConnection::connectToKodi() +{ + m_socket->connectToHost(m_hostAddress, m_port); +} + +void KodiConnection::disconnectFromKodi() +{ + m_socket->close(); +} + +QHostAddress KodiConnection::hostAddress() const +{ + return m_hostAddress; +} + +int KodiConnection::port() const +{ + return m_port; +} + +void KodiConnection::onConnected() +{ + qCDebug(dcKodi) << "connected successfully to" << hostAddress().toString() << port(); + emit connectionStateChanged(true); +} + +void KodiConnection::onDisconnected() +{ + qCDebug(dcKodi) << "disconnected from" << hostAddress().toString() << port(); + emit connectionStateChanged(false); +} + +void KodiConnection::onError(QAbstractSocket::SocketError socketError) +{ + 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(); + + unsigned short m_IconSize; + emit dataReady(data); +} + +void KodiConnection::sendData(const QString &method, const QVariantMap ¶ms) +{ + 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()); +} + diff --git a/plugins/deviceplugins/kodi/kodiconnection.h b/plugins/deviceplugins/kodi/kodiconnection.h new file mode 100644 index 00000000..6142e7e0 --- /dev/null +++ b/plugins/deviceplugins/kodi/kodiconnection.h @@ -0,0 +1,45 @@ +#ifndef KODICONNECTION_H +#define KODICONNECTION_H + +#include +#include +#include +#include + +class KodiConnection : public QObject +{ + Q_OBJECT +public: + explicit KodiConnection(const QHostAddress &hostAddress, const int &port = 9090, QObject *parent = 0); + + void connectToKodi(); + void disconnectFromKodi(); + + QHostAddress hostAddress() const; + int port() const; + +private: + QHostAddress m_hostAddress; + int m_port; + int m_id; + + + + QTcpSocket *m_socket; + +private slots: + void onConnected(); + void onDisconnected(); + void onError(QAbstractSocket::SocketError socketError); + void readData(); + +signals: + void connectionStateChanged(const bool &connected); + void dataReady(const QByteArray &data); + +public slots: + void sendData(const QString &method, const QVariantMap ¶ms = QVariantMap()); + +}; + +#endif // KODICONNECTION_H diff --git a/server/main.cpp b/server/main.cpp index e555d2a1..318f9875 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -81,6 +81,7 @@ int main(int argc, char *argv[]) s_loggingFilters.insert("WakeOnLan", false); s_loggingFilters.insert("Wemo", false); s_loggingFilters.insert("WifiDetector", false); + s_loggingFilters.insert("Kodi", true); QCommandLineParser parser;