Merge PR #669: Kodi: Rework setup
This commit is contained in:
commit
02f9a4ff21
@ -57,77 +57,54 @@ void IntegrationPluginKodi::init()
|
|||||||
m_serviceBrowser = hardwareManager()->zeroConfController()->createServiceBrowser("_xbmc-jsonrpc._tcp");
|
m_serviceBrowser = hardwareManager()->zeroConfController()->createServiceBrowser("_xbmc-jsonrpc._tcp");
|
||||||
m_httpServiceBrowser = hardwareManager()->zeroConfController()->createServiceBrowser("_http._tcp");
|
m_httpServiceBrowser = hardwareManager()->zeroConfController()->createServiceBrowser("_http._tcp");
|
||||||
|
|
||||||
m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(10);
|
connect(m_serviceBrowser, &ZeroConfServiceBrowser::serviceEntryAdded, this, [=](const ZeroConfServiceEntry &entry){
|
||||||
connect(m_pluginTimer, &PluginTimer::timeout, this, &IntegrationPluginKodi::onPluginTimer);
|
QUuid uuid = QUuid(entry.txt("uuid"));
|
||||||
|
foreach (Thing *thing, myThings()) {
|
||||||
|
if (thing->paramValue(kodiThingUuidParamTypeId).toUuid() == uuid) {
|
||||||
|
qCDebug(dcKodi()) << "Kodi" << thing->name() << "appeared on ZeroConf";
|
||||||
|
Kodi *kodi = m_kodis.value(thing);
|
||||||
|
kodi->setHostAddress(entry.hostAddress());
|
||||||
|
kodi->setPort(entry.port());
|
||||||
|
if (!kodi->connected()) {
|
||||||
|
kodi->connectKodi();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void IntegrationPluginKodi::setupThing(ThingSetupInfo *info)
|
void IntegrationPluginKodi::setupThing(ThingSetupInfo *info)
|
||||||
{
|
{
|
||||||
Thing *thing = info->thing();
|
Thing *thing = info->thing();
|
||||||
qCDebug(dcKodi) << "Setup Kodi" << thing->paramValue(kodiThingIpParamTypeId).toString();
|
qCDebug(dcKodi) << "Setthing up Kodi" << thing->paramValue(kodiThingUuidParamTypeId).toString();
|
||||||
|
|
||||||
QUuid kodiUuid = thing->paramValue(kodiThingUuidParamTypeId).toUuid();
|
KodiHostInfo hostInfo = resolve(thing);
|
||||||
|
|
||||||
// The IP string is optional, we'll try to discover it in any case via zeroconf, however, if it's
|
if (info->isInitialSetup()) {
|
||||||
// set in the params, we'll always fall back to that in case we can't find it on zeroconf.
|
if (hostInfo.address.isNull()) {
|
||||||
|
info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Unable to find Kodi in the network."));
|
||||||
// The recommended way is to not store an IP in the settings as with DHCP lease times (or IPv6 privacy
|
return;
|
||||||
// extension address randomization) an IP might expire eventually and it'll stop working.
|
|
||||||
|
|
||||||
// So actually the params should *only* store the UUID, but we'll support manually entering IP, port and http port
|
|
||||||
// for setups that can't use ZeroConf for whatever reason.
|
|
||||||
|
|
||||||
QString ipString = thing->paramValue(kodiThingIpParamTypeId).toString();
|
|
||||||
int port = thing->paramValue(kodiThingPortParamTypeId).toInt();
|
|
||||||
int httpPort = thing->paramValue(kodiThingHttpPortParamTypeId).toInt();
|
|
||||||
|
|
||||||
if (!kodiUuid.isNull()) {
|
|
||||||
foreach (const ZeroConfServiceEntry &entry, m_serviceBrowser->serviceEntries()) {
|
|
||||||
if (entry.hostAddress().protocol() == QAbstractSocket::IPv6Protocol && entry.hostAddress().toString().startsWith("fe80")) {
|
|
||||||
// We don't support link-local ipv6 addresses yet. skip those entries
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
QString uuid;
|
|
||||||
foreach (const QString &txt, entry.txt()) {
|
|
||||||
if (txt.startsWith("uuid")) {
|
|
||||||
uuid = txt.split("=").last();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (QUuid(uuid) == kodiUuid) {
|
|
||||||
ipString = entry.hostAddress().toString();
|
|
||||||
port = entry.port();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach (const ZeroConfServiceEntry avahiEntry, m_httpServiceBrowser->serviceEntries()) {
|
|
||||||
QString uuid;
|
|
||||||
foreach (const QString &txt, avahiEntry.txt()) {
|
|
||||||
if (txt.startsWith("uuid")) {
|
|
||||||
uuid = txt.split("=").last();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (QUuid(uuid) == kodiUuid) {
|
|
||||||
httpPort = avahiEntry.port();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ipString.isEmpty()) {
|
if (m_kodis.contains(thing)) {
|
||||||
// Ok, we could not find an ip on zeroconf... Let's try again in a second while setupInfo hasn't timed out.
|
delete m_kodis.take(thing);
|
||||||
qCDebug(dcKodi()) << "Device not found via ZeroConf... Waiting for a second for it to appear...";
|
}
|
||||||
QTimer::singleShot(1000, info, [this, info](){
|
|
||||||
setupThing(info);
|
qCDebug(dcKodi()).nospace().noquote() << "Connecting to kodi on " << hostInfo.address.toString() << ":" << hostInfo.rpcPort << " (HTTP Port " << hostInfo.httpPort << ")";
|
||||||
|
Kodi *kodi= new Kodi(hostInfo.address, hostInfo.rpcPort, hostInfo.httpPort, thing);
|
||||||
|
m_kodis.insert(thing, kodi);
|
||||||
|
|
||||||
|
if (info->isInitialSetup()) {
|
||||||
|
connect(kodi, &Kodi::connectionStatusChanged, info, [info](bool connected){
|
||||||
|
if (connected) {
|
||||||
|
info->finish(Thing::ThingErrorNoError);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return;
|
} else {
|
||||||
|
info->finish(Thing::ThingErrorNoError);
|
||||||
}
|
}
|
||||||
|
|
||||||
qCDebug(dcKodi()).nospace().noquote() << "Connecting to kodi on " << ipString << ":" << port << " (HTTP Port " << httpPort << ")";
|
|
||||||
Kodi *kodi= new Kodi(QHostAddress(ipString), port, httpPort, this);
|
|
||||||
|
|
||||||
connect(kodi, &Kodi::connectionStatusChanged, this, &IntegrationPluginKodi::onConnectionChanged);
|
connect(kodi, &Kodi::connectionStatusChanged, this, &IntegrationPluginKodi::onConnectionChanged);
|
||||||
connect(kodi, &Kodi::stateChanged, this, &IntegrationPluginKodi::onStateChanged);
|
connect(kodi, &Kodi::stateChanged, this, &IntegrationPluginKodi::onStateChanged);
|
||||||
connect(kodi, &Kodi::actionExecuted, this, &IntegrationPluginKodi::onActionExecuted);
|
connect(kodi, &Kodi::actionExecuted, this, &IntegrationPluginKodi::onActionExecuted);
|
||||||
@ -143,7 +120,7 @@ void IntegrationPluginKodi::setupThing(ThingSetupInfo *info)
|
|||||||
thing->setStateValue(kodiArtistStateTypeId, artist);
|
thing->setStateValue(kodiArtistStateTypeId, artist);
|
||||||
thing->setStateValue(kodiCollectionStateTypeId, collection);
|
thing->setStateValue(kodiCollectionStateTypeId, collection);
|
||||||
|
|
||||||
Kodi* kodi = m_kodis.key(thing);
|
Kodi* kodi = m_kodis.value(thing);
|
||||||
|
|
||||||
QNetworkRequest request;
|
QNetworkRequest request;
|
||||||
QHostAddress hostAddr(kodi->hostAddress().toString());
|
QHostAddress hostAddr(kodi->hostAddress().toString());
|
||||||
@ -153,10 +130,10 @@ void IntegrationPluginKodi::setupThing(ThingSetupInfo *info)
|
|||||||
} else {
|
} else {
|
||||||
addr = "[" + hostAddr.toString() + "]";
|
addr = "[" + hostAddr.toString() + "]";
|
||||||
}
|
}
|
||||||
QString port = thing->paramValue(kodiThingHttpPortParamTypeId).toString();
|
|
||||||
|
|
||||||
request.setUrl(QUrl(QString("http://%1:%2/jsonrpc").arg(addr).arg(port)));
|
uint httpPort = kodi->httpPort();
|
||||||
qCDebug(dcKodi) << "Prepping file dl" << "http://" + addr + ":" + thing->paramValue(kodiThingPortParamTypeId).toString() + "/jsonrpc";
|
request.setUrl(QUrl(QString("http://%1:%2/jsonrpc").arg(addr).arg(httpPort)));
|
||||||
|
qCDebug(dcKodi) << "Prepping file dl" << request.url().toString();
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
QVariantMap map;
|
QVariantMap map;
|
||||||
map.insert("jsonrpc", "2.0");
|
map.insert("jsonrpc", "2.0");
|
||||||
@ -167,10 +144,10 @@ void IntegrationPluginKodi::setupThing(ThingSetupInfo *info)
|
|||||||
map.insert("params", params);
|
map.insert("params", params);
|
||||||
QJsonDocument jsonDoc = QJsonDocument::fromVariant(map);
|
QJsonDocument jsonDoc = QJsonDocument::fromVariant(map);
|
||||||
QNetworkReply *reply = hardwareManager()->networkManager()->post(request, jsonDoc.toJson(QJsonDocument::Compact));
|
QNetworkReply *reply = hardwareManager()->networkManager()->post(request, jsonDoc.toJson(QJsonDocument::Compact));
|
||||||
connect(reply, &QNetworkReply::finished, thing, [thing, reply, addr, port](){
|
connect(reply, &QNetworkReply::finished, thing, [thing, reply, addr, httpPort](){
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll());
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll());
|
||||||
QString fileUrl = "http://" + addr + ":" + port + "/" + jsonDoc.toVariant().toMap().value("result").toMap().value("details").toMap().value("path").toString();
|
QString fileUrl = QString("http://%1:%2/%3").arg(addr).arg(httpPort).arg(jsonDoc.toVariant().toMap().value("result").toMap().value("details").toMap().value("path").toString());
|
||||||
qCDebug(dcKodi()) << "DL result:" << jsonDoc.toJson();
|
qCDebug(dcKodi()) << "DL result:" << jsonDoc.toJson();
|
||||||
qCDebug(dcKodi()) << "Resolved url:" << fileUrl;
|
qCDebug(dcKodi()) << "Resolved url:" << fileUrl;
|
||||||
thing->setStateValue(kodiArtworkStateTypeId, fileUrl);
|
thing->setStateValue(kodiArtworkStateTypeId, fileUrl);
|
||||||
@ -190,37 +167,51 @@ void IntegrationPluginKodi::setupThing(ThingSetupInfo *info)
|
|||||||
thing->setStateValue(kodiRepeatStateTypeId, "None");
|
thing->setStateValue(kodiRepeatStateTypeId, "None");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
m_kodis.insert(kodi, thing);
|
|
||||||
m_asyncSetups.insert(kodi, info);
|
|
||||||
connect(info, &QObject::destroyed, this, [this, kodi](){ m_asyncSetups.remove(kodi); });
|
|
||||||
|
|
||||||
kodi->connectKodi();
|
if (!kodi->hostAddress().isNull()) {
|
||||||
|
kodi->connectKodi();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IntegrationPluginKodi::postSetupThing(Thing */*thing*/)
|
||||||
|
{
|
||||||
|
if (!m_pluginTimer) {
|
||||||
|
m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(10);
|
||||||
|
connect(m_pluginTimer, &PluginTimer::timeout, this, [this](){
|
||||||
|
foreach (Thing *thing, myThings()){
|
||||||
|
Kodi *kodi = m_kodis.value(thing);
|
||||||
|
if (!kodi->connected()) {
|
||||||
|
KodiHostInfo hostInfo = resolve(thing);
|
||||||
|
kodi->setHostAddress(hostInfo.address);
|
||||||
|
kodi->setPort(hostInfo.rpcPort);
|
||||||
|
kodi->setHttpPort(hostInfo.httpPort);
|
||||||
|
kodi->connectKodi();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IntegrationPluginKodi::thingRemoved(Thing *thing)
|
void IntegrationPluginKodi::thingRemoved(Thing *thing)
|
||||||
{
|
{
|
||||||
Kodi *kodi = m_kodis.key(thing);
|
m_kodis.remove(thing);
|
||||||
m_kodis.remove(kodi);
|
|
||||||
qCDebug(dcKodi) << "Delete " << thing->name();
|
if (myThings().isEmpty()) {
|
||||||
kodi->deleteLater();
|
hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
|
||||||
|
m_pluginTimer = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IntegrationPluginKodi::discoverThings(ThingDiscoveryInfo *info)
|
void IntegrationPluginKodi::discoverThings(ThingDiscoveryInfo *info)
|
||||||
{
|
{
|
||||||
QTimer::singleShot(5000, info, [this, info](){
|
QTimer::singleShot(5000, info, [this, info](){
|
||||||
|
|
||||||
QHash<QString, ThingDescriptor> descriptors;
|
QHash<QUuid, ThingDescriptor> descriptors;
|
||||||
|
|
||||||
foreach (const ZeroConfServiceEntry avahiEntry, m_serviceBrowser->serviceEntries()) {
|
foreach (const ZeroConfServiceEntry avahiEntry, m_serviceBrowser->serviceEntries()) {
|
||||||
|
|
||||||
QString uuid;
|
QUuid uuid = avahiEntry.txt("uuid");
|
||||||
foreach (const QString &txt, avahiEntry.txt()) {
|
|
||||||
if (txt.startsWith("uuid")) {
|
|
||||||
uuid = txt.split("=").last();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (descriptors.contains(uuid)) {
|
if (descriptors.contains(uuid)) {
|
||||||
// Might appear multiple times, IPv4 and IPv6
|
// Might appear multiple times, IPv4 and IPv6
|
||||||
continue;
|
continue;
|
||||||
@ -230,8 +221,6 @@ void IntegrationPluginKodi::discoverThings(ThingDiscoveryInfo *info)
|
|||||||
ThingDescriptor descriptor(kodiThingClassId, avahiEntry.name(), avahiEntry.hostName() + " (" + avahiEntry.hostAddress().toString() + ")");
|
ThingDescriptor descriptor(kodiThingClassId, avahiEntry.name(), avahiEntry.hostName() + " (" + avahiEntry.hostAddress().toString() + ")");
|
||||||
ParamList params;
|
ParamList params;
|
||||||
params << Param(kodiThingUuidParamTypeId, uuid);
|
params << Param(kodiThingUuidParamTypeId, uuid);
|
||||||
// params << Param(kodiThingIpParamTypeId, avahiEntry.hostAddress().toString());
|
|
||||||
params << Param(kodiThingPortParamTypeId, avahiEntry.port());
|
|
||||||
descriptor.setParams(params);
|
descriptor.setParams(params);
|
||||||
|
|
||||||
Things existing = myThings().filterByParam(kodiThingUuidParamTypeId, uuid);
|
Things existing = myThings().filterByParam(kodiThingUuidParamTypeId, uuid);
|
||||||
@ -242,26 +231,6 @@ void IntegrationPluginKodi::discoverThings(ThingDiscoveryInfo *info)
|
|||||||
descriptors.insert(uuid, descriptor);
|
descriptors.insert(uuid, descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (const ZeroConfServiceEntry avahiEntry, m_httpServiceBrowser->serviceEntries()) {
|
|
||||||
qCDebug(dcKodi) << "Zeroconf http entry:" << avahiEntry;
|
|
||||||
QString uuid;
|
|
||||||
foreach (const QString &txt, avahiEntry.txt()) {
|
|
||||||
if (txt.startsWith("uuid")) {
|
|
||||||
uuid = txt.split("=").last();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!descriptors.contains(uuid)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
qCDebug(dcKodi()) << "Updating http parameter:" << avahiEntry.port();
|
|
||||||
ThingDescriptor descriptor = descriptors.value(uuid);
|
|
||||||
ParamList params = descriptor.params();
|
|
||||||
params << Param(kodiThingHttpPortParamTypeId, avahiEntry.port());
|
|
||||||
descriptor.setParams(params);
|
|
||||||
descriptors[uuid] = descriptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (const ThingDescriptor &d, descriptors.values()) {
|
foreach (const ThingDescriptor &d, descriptors.values()) {
|
||||||
qCDebug(dcKodi()) << "Returning descritpor:" << d.params();
|
qCDebug(dcKodi()) << "Returning descritpor:" << d.params();
|
||||||
}
|
}
|
||||||
@ -274,7 +243,7 @@ void IntegrationPluginKodi::executeAction(ThingActionInfo *info)
|
|||||||
{
|
{
|
||||||
Action action = info->action();
|
Action action = info->action();
|
||||||
Thing *thing = info->thing();
|
Thing *thing = info->thing();
|
||||||
Kodi *kodi = m_kodis.key(thing);
|
Kodi *kodi = m_kodis.value(thing);
|
||||||
|
|
||||||
// check connection state
|
// check connection state
|
||||||
if (!kodi->connected()) {
|
if (!kodi->connected()) {
|
||||||
@ -351,7 +320,7 @@ void IntegrationPluginKodi::executeAction(ThingActionInfo *info)
|
|||||||
|
|
||||||
void IntegrationPluginKodi::browseThing(BrowseResult *result)
|
void IntegrationPluginKodi::browseThing(BrowseResult *result)
|
||||||
{
|
{
|
||||||
Kodi *kodi = m_kodis.key(result->thing());
|
Kodi *kodi = m_kodis.value(result->thing());
|
||||||
if (!kodi) {
|
if (!kodi) {
|
||||||
result->finish(Thing::ThingErrorHardwareNotAvailable);
|
result->finish(Thing::ThingErrorHardwareNotAvailable);
|
||||||
return;
|
return;
|
||||||
@ -362,7 +331,7 @@ void IntegrationPluginKodi::browseThing(BrowseResult *result)
|
|||||||
|
|
||||||
void IntegrationPluginKodi::browserItem(BrowserItemResult *result)
|
void IntegrationPluginKodi::browserItem(BrowserItemResult *result)
|
||||||
{
|
{
|
||||||
Kodi *kodi = m_kodis.key(result->thing());
|
Kodi *kodi = m_kodis.value(result->thing());
|
||||||
if (!kodi) {
|
if (!kodi) {
|
||||||
result->finish(Thing::ThingErrorHardwareNotAvailable);
|
result->finish(Thing::ThingErrorHardwareNotAvailable);
|
||||||
return;
|
return;
|
||||||
@ -373,7 +342,7 @@ void IntegrationPluginKodi::browserItem(BrowserItemResult *result)
|
|||||||
|
|
||||||
void IntegrationPluginKodi::executeBrowserItem(BrowserActionInfo *info)
|
void IntegrationPluginKodi::executeBrowserItem(BrowserActionInfo *info)
|
||||||
{
|
{
|
||||||
Kodi *kodi = m_kodis.key(info->thing());
|
Kodi *kodi = m_kodis.value(info->thing());
|
||||||
if (!kodi) {
|
if (!kodi) {
|
||||||
info->finish(Thing::ThingErrorHardwareNotAvailable);
|
info->finish(Thing::ThingErrorHardwareNotAvailable);
|
||||||
return;
|
return;
|
||||||
@ -389,7 +358,7 @@ void IntegrationPluginKodi::executeBrowserItem(BrowserActionInfo *info)
|
|||||||
|
|
||||||
void IntegrationPluginKodi::executeBrowserItemAction(BrowserItemActionInfo *info)
|
void IntegrationPluginKodi::executeBrowserItemAction(BrowserItemActionInfo *info)
|
||||||
{
|
{
|
||||||
Kodi *kodi = m_kodis.key(info->thing());
|
Kodi *kodi = m_kodis.value(info->thing());
|
||||||
if (!kodi) {
|
if (!kodi) {
|
||||||
return info->finish(Thing::ThingErrorHardwareNotAvailable);
|
return info->finish(Thing::ThingErrorHardwareNotAvailable);
|
||||||
}
|
}
|
||||||
@ -402,48 +371,83 @@ void IntegrationPluginKodi::executeBrowserItemAction(BrowserItemActionInfo *info
|
|||||||
connect(info, &QObject::destroyed, this, [this, id](){ m_pendingBrowserItemActions.remove(id); });
|
connect(info, &QObject::destroyed, this, [this, id](){ m_pendingBrowserItemActions.remove(id); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void IntegrationPluginKodi::onPluginTimer()
|
IntegrationPluginKodi::KodiHostInfo IntegrationPluginKodi::resolve(Thing *thing)
|
||||||
{
|
{
|
||||||
foreach (Kodi *kodi, m_kodis.keys()) {
|
QUuid uuid = thing->paramValue(kodiThingUuidParamTypeId).toUuid();
|
||||||
if (!kodi->connected()) {
|
|
||||||
kodi->connectKodi();
|
KodiHostInfo ret;
|
||||||
|
|
||||||
|
foreach (const ZeroConfServiceEntry &entry, m_serviceBrowser->serviceEntries()) {
|
||||||
|
//Disabling IPv6 for now... it seems to be to unreliable
|
||||||
|
if (entry.hostAddress().protocol() == QAbstractSocket::IPv6Protocol) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QUuid entryUuid = entry.txt("uuid");
|
||||||
|
if (entryUuid != uuid) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.address = entry.hostAddress();
|
||||||
|
ret.rpcPort = entry.port();
|
||||||
|
|
||||||
|
foreach (const ZeroConfServiceEntry &httpEntry, m_httpServiceBrowser->serviceEntries()) {
|
||||||
|
if (QUuid(httpEntry.txt("uuid")) == uuid) {
|
||||||
|
ret.httpPort = httpEntry.port();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ret.address.isNull()) {
|
||||||
|
if (pluginStorage()->childGroups().contains(thing->id().toString())) {
|
||||||
|
pluginStorage()->beginGroup(thing->id().toString());
|
||||||
|
ret.address = pluginStorage()->value("address").toString();
|
||||||
|
ret.rpcPort = pluginStorage()->value("rpcPort").toUInt();
|
||||||
|
ret.httpPort = pluginStorage()->value("httpPort").toUInt();
|
||||||
|
pluginStorage()->endGroup();
|
||||||
|
qCDebug(dcKodi()) << "Kodi not found on ZeroConf. Using cached info:" << ret.address.toString() << ret.rpcPort << ret.httpPort;
|
||||||
|
} else {
|
||||||
|
qCDebug(dcKodi()) << "Kodi not found on ZeroConf and no cached info availble";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qCDebug(dcKodi()) << "Kodie address resolved:" << ret.address.toString();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IntegrationPluginKodi::onConnectionChanged(bool connected)
|
void IntegrationPluginKodi::onConnectionChanged(bool connected)
|
||||||
{
|
{
|
||||||
|
qCDebug(dcKodi()) << "Connection status changed:" << connected;
|
||||||
Kodi *kodi = static_cast<Kodi *>(sender());
|
Kodi *kodi = static_cast<Kodi *>(sender());
|
||||||
Thing *thing = m_kodis.value(kodi);
|
Thing *thing = m_kodis.key(kodi);
|
||||||
|
|
||||||
// Finish setup
|
thing->setStateValue(kodiConnectedStateTypeId, connected);
|
||||||
ThingSetupInfo *info = m_asyncSetups.value(kodi);
|
|
||||||
if (info) {
|
|
||||||
if (connected) {
|
if (connected) {
|
||||||
info->finish(Thing::ThingErrorNoError);
|
pluginStorage()->beginGroup(thing->id().toString());
|
||||||
|
pluginStorage()->setValue("address", kodi->hostAddress().toString());
|
||||||
|
pluginStorage()->setValue("rpcPort", kodi->port());
|
||||||
|
pluginStorage()->setValue("httpPort", kodi->httpPort());
|
||||||
|
pluginStorage()->endGroup();
|
||||||
|
|
||||||
|
QString imageString;
|
||||||
|
QUrl notificationUrl = thing->setting(kodiSettingsNotificationCustomIconUrlParamTypeId).toUrl();
|
||||||
|
if (!notificationUrl.isEmpty() && notificationUrl.isValid()) {
|
||||||
|
imageString = notificationUrl.toString();
|
||||||
} else {
|
} else {
|
||||||
//: Error setting up thing
|
imageString = "info";
|
||||||
m_asyncSetups.take(kodi)->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("This installation of Kodi is too old. Please upgrade your Kodi system."));
|
|
||||||
}
|
}
|
||||||
|
kodi->showNotification("nymea", "Connected", 2000, imageString);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString imageString;
|
|
||||||
QUrl notificationUrl = thing->setting(kodiSettingsNotificationCustomIconUrlParamTypeId).toUrl();
|
|
||||||
if (!notificationUrl.isEmpty() && notificationUrl.isValid()) {
|
|
||||||
imageString = notificationUrl.toString();
|
|
||||||
} else {
|
|
||||||
imageString = "info";
|
|
||||||
}
|
|
||||||
|
|
||||||
kodi->showNotification("nymea", QT_TR_NOOP("Connected"), 2000, imageString);
|
|
||||||
thing->setStateValue(kodiConnectedStateTypeId, kodi->connected());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IntegrationPluginKodi::onStateChanged()
|
void IntegrationPluginKodi::onStateChanged()
|
||||||
{
|
{
|
||||||
Kodi *kodi = static_cast<Kodi *>(sender());
|
Kodi *kodi = static_cast<Kodi *>(sender());
|
||||||
Thing *thing = m_kodis.value(kodi);
|
Thing *thing = m_kodis.key(kodi);
|
||||||
|
|
||||||
// set thing state values
|
// set thing state values
|
||||||
thing->setStateValue(kodiVolumeStateTypeId, kodi->volume());
|
thing->setStateValue(kodiVolumeStateTypeId, kodi->volume());
|
||||||
@ -477,7 +481,7 @@ void IntegrationPluginKodi::onBrowserItemActionExecuted(int actionId, bool succe
|
|||||||
void IntegrationPluginKodi::onPlaybackStatusChanged(const QString &playbackStatus)
|
void IntegrationPluginKodi::onPlaybackStatusChanged(const QString &playbackStatus)
|
||||||
{
|
{
|
||||||
Kodi *kodi = static_cast<Kodi *>(sender());
|
Kodi *kodi = static_cast<Kodi *>(sender());
|
||||||
Thing *thing = m_kodis.value(kodi);
|
Thing *thing = m_kodis.key(kodi);
|
||||||
thing->setStateValue(kodiPlaybackStatusStateTypeId, playbackStatus);
|
thing->setStateValue(kodiPlaybackStatusStateTypeId, playbackStatus);
|
||||||
// legacy events
|
// legacy events
|
||||||
if (playbackStatus == "Playing") {
|
if (playbackStatus == "Playing") {
|
||||||
|
|||||||
@ -53,6 +53,7 @@ public:
|
|||||||
|
|
||||||
void init() override;
|
void init() override;
|
||||||
void setupThing(ThingSetupInfo *info) override;
|
void setupThing(ThingSetupInfo *info) override;
|
||||||
|
void postSetupThing(Thing *thing) override;
|
||||||
void thingRemoved(Thing *thing) override;
|
void thingRemoved(Thing *thing) override;
|
||||||
void discoverThings(ThingDiscoveryInfo *info) override;
|
void discoverThings(ThingDiscoveryInfo *info) override;
|
||||||
void executeAction(ThingActionInfo *info) override;
|
void executeAction(ThingActionInfo *info) override;
|
||||||
@ -63,9 +64,17 @@ public:
|
|||||||
void executeBrowserItemAction(BrowserItemActionInfo *info) override;
|
void executeBrowserItemAction(BrowserItemActionInfo *info) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PluginTimer *m_pluginTimer;
|
struct KodiHostInfo {
|
||||||
QHash<Kodi*, Thing*> m_kodis;
|
QHostAddress address;
|
||||||
QHash<Kodi*, ThingSetupInfo*> m_asyncSetups;
|
uint rpcPort = 9090;
|
||||||
|
uint httpPort = 8080;
|
||||||
|
};
|
||||||
|
|
||||||
|
KodiHostInfo resolve(Thing *thing);
|
||||||
|
|
||||||
|
private:
|
||||||
|
PluginTimer *m_pluginTimer = nullptr;
|
||||||
|
QHash<Thing*, Kodi*> m_kodis;
|
||||||
ZeroConfServiceBrowser *m_serviceBrowser = nullptr;
|
ZeroConfServiceBrowser *m_serviceBrowser = nullptr;
|
||||||
ZeroConfServiceBrowser *m_httpServiceBrowser = nullptr;
|
ZeroConfServiceBrowser *m_httpServiceBrowser = nullptr;
|
||||||
|
|
||||||
@ -74,7 +83,6 @@ private:
|
|||||||
QHash<int, BrowserItemActionInfo*> m_pendingBrowserItemActions;
|
QHash<int, BrowserItemActionInfo*> m_pendingBrowserItemActions;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onPluginTimer();
|
|
||||||
void onConnectionChanged(bool connected);
|
void onConnectionChanged(bool connected);
|
||||||
void onStateChanged();
|
void onStateChanged();
|
||||||
void onActionExecuted(int actionId, bool success);
|
void onActionExecuted(int actionId, bool success);
|
||||||
|
|||||||
@ -16,27 +16,6 @@
|
|||||||
"createMethods": ["user", "discovery"],
|
"createMethods": ["user", "discovery"],
|
||||||
"browsable": true,
|
"browsable": true,
|
||||||
"paramTypes": [
|
"paramTypes": [
|
||||||
{
|
|
||||||
"id": "1a897065-57c6-49b3-bac9-1e5db27859e5",
|
|
||||||
"name": "ip",
|
|
||||||
"displayName": "IP Address",
|
|
||||||
"type" : "QString",
|
|
||||||
"inputType": "IPv4Address",
|
|
||||||
"defaultValue": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "660fb4d7-9479-4c9d-a900-ce221d2b8ae4",
|
|
||||||
"name": "port",
|
|
||||||
"displayName": "Port",
|
|
||||||
"type" : "int"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "27ea7e46-80f0-49ea-9352-b57c78905c67",
|
|
||||||
"name": "httpPort",
|
|
||||||
"displayName": "HTTP port",
|
|
||||||
"type" : "int",
|
|
||||||
"defaultValue": 8080
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "692eb6e0-7f4e-4f43-92da-8347372287ce",
|
"id": "692eb6e0-7f4e-4f43-92da-8347372287ce",
|
||||||
"name": "uuid",
|
"name": "uuid",
|
||||||
|
|||||||
@ -186,11 +186,31 @@ QHostAddress Kodi::hostAddress() const
|
|||||||
return m_connection->hostAddress();
|
return m_connection->hostAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
int Kodi::port() const
|
void Kodi::setHostAddress(const QHostAddress &address)
|
||||||
|
{
|
||||||
|
m_connection->setHostAddress(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint Kodi::port() const
|
||||||
{
|
{
|
||||||
return m_connection->port();
|
return m_connection->port();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Kodi::setPort(uint port)
|
||||||
|
{
|
||||||
|
m_connection->setPort(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint Kodi::httpPort() const
|
||||||
|
{
|
||||||
|
return m_httpPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kodi::setHttpPort(uint httpPort)
|
||||||
|
{
|
||||||
|
m_httpPort = httpPort;
|
||||||
|
}
|
||||||
|
|
||||||
bool Kodi::connected() const
|
bool Kodi::connected() const
|
||||||
{
|
{
|
||||||
return m_connection->connected();
|
return m_connection->connected();
|
||||||
|
|||||||
@ -52,7 +52,13 @@ public:
|
|||||||
~Kodi();
|
~Kodi();
|
||||||
|
|
||||||
QHostAddress hostAddress() const;
|
QHostAddress hostAddress() const;
|
||||||
int port() const;
|
void setHostAddress(const QHostAddress &address);
|
||||||
|
|
||||||
|
uint port() const;
|
||||||
|
void setPort(uint port);
|
||||||
|
|
||||||
|
uint httpPort() const;
|
||||||
|
void setHttpPort(uint httpPort);
|
||||||
|
|
||||||
bool connected() const;
|
bool connected() const;
|
||||||
|
|
||||||
|
|||||||
@ -51,6 +51,7 @@ KodiConnection::KodiConnection(const QHostAddress &hostAddress, int port, QObjec
|
|||||||
void KodiConnection::connectKodi()
|
void KodiConnection::connectKodi()
|
||||||
{
|
{
|
||||||
if (m_socket->state() == QAbstractSocket::ConnectingState) {
|
if (m_socket->state() == QAbstractSocket::ConnectingState) {
|
||||||
|
qCDebug(dcKodi) << "Aready connecting... skipping request";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_socket->connectToHost(m_hostAddress, m_port);
|
m_socket->connectToHost(m_hostAddress, m_port);
|
||||||
@ -66,11 +67,22 @@ QHostAddress KodiConnection::hostAddress() const
|
|||||||
return m_hostAddress;
|
return m_hostAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void KodiConnection::setHostAddress(const QHostAddress &address)
|
||||||
|
{
|
||||||
|
m_hostAddress = address;
|
||||||
|
}
|
||||||
|
|
||||||
int KodiConnection::port() const
|
int KodiConnection::port() const
|
||||||
{
|
{
|
||||||
return m_port;
|
return m_port;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void KodiConnection::setPort(int port)
|
||||||
|
{
|
||||||
|
m_port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool KodiConnection::connected()
|
bool KodiConnection::connected()
|
||||||
{
|
{
|
||||||
return m_connected;
|
return m_connected;
|
||||||
@ -92,9 +104,9 @@ void KodiConnection::onDisconnected()
|
|||||||
|
|
||||||
void KodiConnection::onError(QAbstractSocket::SocketError socketError)
|
void KodiConnection::onError(QAbstractSocket::SocketError socketError)
|
||||||
{
|
{
|
||||||
if (connected()) {
|
qCWarning(dcKodi) << "socket error:" << socketError << m_socket->errorString() << "this" << this;
|
||||||
qCWarning(dcKodi) << "socket error:" << socketError << m_socket->errorString();
|
m_connected = false;
|
||||||
}
|
emit connectionStatusChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void KodiConnection::readData()
|
void KodiConnection::readData()
|
||||||
|
|||||||
@ -46,8 +46,10 @@ public:
|
|||||||
void disconnectKodi();
|
void disconnectKodi();
|
||||||
|
|
||||||
QHostAddress hostAddress() const;
|
QHostAddress hostAddress() const;
|
||||||
|
void setHostAddress(const QHostAddress &address);
|
||||||
|
|
||||||
int port() const;
|
int port() const;
|
||||||
int httpPort() const;
|
void setPort(int port);
|
||||||
|
|
||||||
bool connected();
|
bool connected();
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user