Fix UDAP Lg smart tv and start implementing browsable

This commit is contained in:
Simon Stürz 2021-07-25 17:02:52 +02:00
parent 6d02d18df0
commit 40a435ee9c
7 changed files with 699 additions and 286 deletions

View File

@ -55,16 +55,10 @@ void IntegrationPluginLgSmartTv::init()
void IntegrationPluginLgSmartTv::discoverThings(ThingDiscoveryInfo *info)
{
if (info->thingClassId() == lgSmartTvThingClassId) {
qCDebug(dcLgSmartTv()) << "Start discovering";
qCDebug(dcLgSmartTv()) << "Start discovering LG UDAP 2.0 smart TVs...";
UpnpDiscoveryReply *reply = hardwareManager()->upnpDiscovery()->discoverDevices("udap:rootservice","UDAP/2.0");
// Clean up in any case when the reply finishes
connect(reply, &UpnpDiscoveryReply::finished, reply, &UpnpDiscoveryReply::deleteLater);
// Connect reply to discovery info for rsults.
connect(reply, &UpnpDiscoveryReply::finished, info, [this, info, reply](){
if (reply->error() != UpnpDiscoveryReply::UpnpDiscoveryReplyErrorNoError) {
qCWarning(dcLgSmartTv()) << "Upnp discovery error" << reply->error();
return info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Error discovering devices. Please check your network connection."));
@ -75,13 +69,14 @@ void IntegrationPluginLgSmartTv::discoverThings(ThingDiscoveryInfo *info)
continue;
qCDebug(dcLgSmartTv) << upnpDeviceDescriptor;
ThingDescriptor descriptor(lgSmartTvThingClassId, "Lg Smart Tv", upnpDeviceDescriptor.modelName());
ThingDescriptor descriptor(lgSmartTvThingClassId, "LG Smart Tv", upnpDeviceDescriptor.modelName());
ParamList params;
params << Param(lgSmartTvThingNameParamTypeId, upnpDeviceDescriptor.friendlyName());
params << Param(lgSmartTvThingUuidParamTypeId, upnpDeviceDescriptor.uuid());
params << Param(lgSmartTvThingModelParamTypeId, upnpDeviceDescriptor.modelName());
params << Param(lgSmartTvThingHostAddressParamTypeId, upnpDeviceDescriptor.hostAddress().toString());
params << Param(lgSmartTvThingPortParamTypeId, upnpDeviceDescriptor.port());
params << Param(lgSmartTvThingEventHandlerPortParamTypeId, TvEventHandler::getFreePort());
descriptor.setParams(params);
foreach (Thing *existingThing, myThings()) {
@ -96,7 +91,7 @@ void IntegrationPluginLgSmartTv::discoverThings(ThingDiscoveryInfo *info)
});
} else if (info->thingClassId() == webosTvThingClassId) {
qCDebug(dcLgSmartTv()) << "Start discovering Webos tv";
qCDebug(dcLgSmartTv()) << "Start discovering WebOs smart TVs...";
UpnpDiscoveryReply *reply = hardwareManager()->upnpDiscovery()->discoverDevices("");
connect(reply, &UpnpDiscoveryReply::finished, reply, &UpnpDiscoveryReply::deleteLater);
connect(reply, &UpnpDiscoveryReply::finished, info, [this, info, reply](){
@ -116,68 +111,69 @@ void IntegrationPluginLgSmartTv::discoverThings(ThingDiscoveryInfo *info)
info->finish(Thing::ThingErrorNoError);
});
}
}
void IntegrationPluginLgSmartTv::startPairing(ThingPairingInfo *info)
{
QHostAddress host = QHostAddress(info->params().paramValue(lgSmartTvThingHostAddressParamTypeId).toString());
int port = info->params().paramValue(lgSmartTvThingPortParamTypeId).toInt();
QPair<QNetworkRequest, QByteArray> request = TvDevice::createDisplayKeyRequest(host, port);
if (info->thingClassId() == lgSmartTvThingClassId) {
QHostAddress host = QHostAddress(info->params().paramValue(lgSmartTvThingHostAddressParamTypeId).toString());
quint16 port = info->params().paramValue(lgSmartTvThingPortParamTypeId).toUInt();
QPair<QNetworkRequest, QByteArray> request = TvDevice::createDisplayKeyRequest(host, port);
QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, info, [info, reply](){
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (status != 200) {
qCWarning(dcLgSmartTv) << "display pin on TV request error:" << status << reply->errorString();
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Error connecting to the TV."));
return;
}
connect(reply, &QNetworkReply::finished, info, [info, reply](){
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (status != 200) {
qCWarning(dcLgSmartTv) << "display pin on TV request error:" << status << reply->errorString();
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Error connecting to the TV."));
return;
}
info->finish(Thing::ThingErrorNoError, QT_TR_NOOP("Please enter the key displayed on the TV."));
});
info->finish(Thing::ThingErrorNoError, QT_TR_NOOP("Please enter the key displayed on the TV."));
});
}
}
void IntegrationPluginLgSmartTv::confirmPairing(ThingPairingInfo *info, const QString &username, const QString &secret)
{
Q_UNUSED(username)
QHostAddress host = QHostAddress(info->params().paramValue(lgSmartTvThingHostAddressParamTypeId).toString());
int port = info->params().paramValue(lgSmartTvThingPortParamTypeId).toInt();
QPair<QNetworkRequest, QByteArray> request = TvDevice::createPairingRequest(host, port, secret);
QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, info, [this, info, reply, secret](){
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if(status != 200) {
qCWarning(dcLgSmartTv) << "pair TV request error:" << status << reply->errorString();
return info->finish(Thing::ThingErrorAuthenticationFailure, QT_TR_NOOP("Error pairing TV. Please try again."));
}
// End pairing before calling setupDevice, which will always try to pair
QPair<QNetworkRequest, QByteArray> request = TvDevice::createEndPairingRequest(reply->request().url());
if (info->thingClassId() == lgSmartTvThingClassId) {
QHostAddress host = QHostAddress(info->params().paramValue(lgSmartTvThingHostAddressParamTypeId).toString());
quint16 port = info->params().paramValue(lgSmartTvThingPortParamTypeId).toUInt();
quint16 eventHandlerPort = info->params().paramValue(lgSmartTvThingEventHandlerPortParamTypeId).toUInt();
QPair<QNetworkRequest, QByteArray> request = TvDevice::createPairingRequest(host, port, eventHandlerPort, secret);
QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, info, [this, info, reply, secret](){
connect(reply, &QNetworkReply::finished, info, [=](){
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if(status != 200) {
qCWarning(dcLgSmartTv) << "end pairing TV request error:" << status << reply->errorString();
info->finish(Thing::ThingErrorAuthenticationFailure, QT_TR_NOOP("Error pairing TV. Please try again."));
return;
qCWarning(dcLgSmartTv) << "Pair TV request error:" << status << reply->errorString();
return info->finish(Thing::ThingErrorAuthenticationFailure, QT_TR_NOOP("Error pairing TV. Please try again."));
}
pluginStorage()->beginGroup(info->thingId().toString());
pluginStorage()->setValue("key", secret);
pluginStorage()->endGroup();
// End pairing before calling setupDevice, which will always try to pair
QPair<QNetworkRequest, QByteArray> request = TvDevice::createEndPairingRequest(host, port, eventHandlerPort);
QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, info, [this, info, reply, secret](){
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if(status != 200) {
qCWarning(dcLgSmartTv) << "End pairing TV request error:" << status << reply->errorString();
info->finish(Thing::ThingErrorAuthenticationFailure, QT_TR_NOOP("Error pairing TV. Please try again."));
return;
}
info->finish(Thing::ThingErrorNoError);
pluginStorage()->beginGroup(info->thingId().toString());
pluginStorage()->setValue("key", secret);
pluginStorage()->endGroup();
info->finish(Thing::ThingErrorNoError);
});
});
});
}
}
void IntegrationPluginLgSmartTv::setupThing(ThingSetupInfo *info)
@ -187,7 +183,9 @@ void IntegrationPluginLgSmartTv::setupThing(ThingSetupInfo *info)
if (thing->thingClassId() == lgSmartTvThingClassId) {
qCDebug(dcLgSmartTv()) << "Setup LG smart TV" << thing->name() << thing->params();
QHostAddress address = QHostAddress(thing->paramValue(lgSmartTvThingHostAddressParamTypeId).toString());
TvDevice *tvDevice = new TvDevice(address, thing->paramValue(lgSmartTvThingPortParamTypeId).toInt(), this);
quint16 port = thing->paramValue(lgSmartTvThingPortParamTypeId).toUInt();
quint16 eventHandlerPort = thing->paramValue(lgSmartTvThingEventHandlerPortParamTypeId).toUInt();
TvDevice *tvDevice = new TvDevice(address, port, eventHandlerPort, this);
tvDevice->setUuid(thing->paramValue(lgSmartTvThingUuidParamTypeId).toString());
pluginStorage()->beginGroup(thing->id().toString());
@ -199,7 +197,6 @@ void IntegrationPluginLgSmartTv::setupThing(ThingSetupInfo *info)
connect(tvDevice, &TvDevice::stateChanged, this, &IntegrationPluginLgSmartTv::stateChanged);
m_tvList.insert(tvDevice, thing);
if (!m_pluginTimer) {
m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(5);
connect(m_pluginTimer, &PluginTimer::timeout, this, &IntegrationPluginLgSmartTv::onPluginTimer);
@ -207,9 +204,7 @@ void IntegrationPluginLgSmartTv::setupThing(ThingSetupInfo *info)
info->finish(Thing::ThingErrorNoError);
return;
}
if (thing->thingClassId() == webosTvThingClassId) {
} else if (thing->thingClassId() == webosTvThingClassId) {
QHostAddress hostAddress = QHostAddress(thing->paramValue(webosTvThingHostAddressParamTypeId).toString());
WebosConnection *webosConnection = new WebosConnection(this);
webosConnection->setHostAddress(hostAddress);
@ -262,7 +257,6 @@ void IntegrationPluginLgSmartTv::executeAction(ThingActionInfo *info)
Action action = info->action();
if (thing->thingClassId() == lgSmartTvThingClassId) {
TvDevice * tvDevice = m_tvList.key(thing);
if (!tvDevice->reachable()) {
qCWarning(dcLgSmartTv) << "Device not reachable";
@ -270,59 +264,79 @@ void IntegrationPluginLgSmartTv::executeAction(ThingActionInfo *info)
}
QNetworkReply *reply = nullptr;
if (action.actionTypeId() == lgSmartTvCommandVolumeUpActionTypeId) {
if (action.actionTypeId() == lgSmartTvIncreaseVolumeActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::VolUp);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandVolumeDownActionTypeId) {
} else if (action.actionTypeId() == lgSmartTvDecreaseVolumeActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::VolDown);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandMuteActionTypeId) {
} else if (action.actionTypeId() == lgSmartTvMuteActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Mute);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandChannelUpActionTypeId) {
} else if (action.actionTypeId() == lgSmartTvCommandChannelUpActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::ChannelUp);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandChannelDownActionTypeId) {
} else if (action.actionTypeId() == lgSmartTvCommandChannelDownActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::ChannelDown);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandPowerOffActionTypeId) {
} else if (action.actionTypeId() == lgSmartTvCommandPowerOffActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Power);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandArrowUpActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Up);
} else if (action.actionTypeId() == lgSmartTvNavigateActionTypeId) {
TvDevice::RemoteKey key;
QString navigateTo = action.paramValue(lgSmartTvNavigateActionToParamTypeId).toString();
if (navigateTo == "up") {
key = TvDevice::Up;
} else if (navigateTo == "down") {
key = TvDevice::Down;
} else if (navigateTo == "left") {
key = TvDevice::Left;
} else if (navigateTo == "right") {
key = TvDevice::Right;
} else if (navigateTo == "enter") {
key = TvDevice::Ok;
} else if (navigateTo == "back") {
key = TvDevice::Back;
} else if (navigateTo == "menu") {
key = TvDevice::Menu;
} else if (navigateTo == "info") {
key = TvDevice::Info;
} else if (navigateTo == "home") {
key = TvDevice::Home;
}
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(key);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandArrowDownActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Down);
} else if (action.actionTypeId() == lgSmartTvPlayActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Play);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandArrowLeftActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Left);
} else if (action.actionTypeId() == lgSmartTvPauseActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Pause);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandArrowRightActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Right);
} else if (action.actionTypeId() == lgSmartTvStopActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Stop);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandOkActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Ok);
} else if (action.actionTypeId() == lgSmartTvSkipNextActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::SkipForward);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandBackActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Back);
} else if (action.actionTypeId() == lgSmartTvSkipBackActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::SkipBackward);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandHomeActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Home);
} else if (action.actionTypeId() == lgSmartTvFastForwardActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::FastForward);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandInputSourceActionTypeId) {
} else if (action.actionTypeId() == lgSmartTvFastRewindActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Rewind);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if (action.actionTypeId() == lgSmartTvCommandInputSourceActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::ExternalInput);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandExitActionTypeId) {
} else if (action.actionTypeId() == lgSmartTvCommandExitActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Exit);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandInfoActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Info);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandMyAppsActionTypeId) {
} else if (action.actionTypeId() == lgSmartTvCommandMyAppsActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::MyApps);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandProgramListActionTypeId) {
} else if (action.actionTypeId() == lgSmartTvCommandProgramListActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::ProgramList);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
}
@ -347,61 +361,268 @@ void IntegrationPluginLgSmartTv::executeAction(ThingActionInfo *info)
}
}
void IntegrationPluginLgSmartTv::browseThing(BrowseResult *result)
{
if (result->thing()->thingClassId() == lgSmartTvThingClassId) {
TvDevice *tv = m_tvList.key(result->thing());
if (!tv) {
result->finish(Thing::ThingErrorHardwareNotAvailable);
return;
}
tv->browse(result);
}
}
void IntegrationPluginLgSmartTv::browserItem(BrowserItemResult *result)
{
if (result->thing()->thingClassId() == lgSmartTvThingClassId) {
TvDevice *tv = m_tvList.key(result->thing());
if (!tv) {
result->finish(Thing::ThingErrorHardwareNotAvailable);
return;
}
tv->browserItem(result);
}
}
void IntegrationPluginLgSmartTv::pairTvDevice(Thing *thing)
{
qCDebug(dcLgSmartTv()) << "Send pair request TV" << thing->name();
QHostAddress host = QHostAddress(thing->paramValue(lgSmartTvThingHostAddressParamTypeId).toString());
int port = thing->paramValue(lgSmartTvThingPortParamTypeId).toInt();
if (thing->thingClassId() == lgSmartTvThingClassId) {
qCDebug(dcLgSmartTv()) << "Send pair request TV" << thing->name();
QHostAddress host = QHostAddress(thing->paramValue(lgSmartTvThingHostAddressParamTypeId).toString());
quint16 port = thing->paramValue(lgSmartTvThingPortParamTypeId).toUInt();
quint16 eventHandlerPort = thing->paramValue(lgSmartTvThingEventHandlerPortParamTypeId).toUInt();
pluginStorage()->beginGroup(thing->id().toString());
QString key = pluginStorage()->value("key").toString();
pluginStorage()->endGroup();
pluginStorage()->beginGroup(thing->id().toString());
QString key = pluginStorage()->value("key").toString();
pluginStorage()->endGroup();
QPair<QNetworkRequest, QByteArray> request = TvDevice::createPairingRequest(host, port, key);
QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
connect(reply, &QNetworkReply::finished, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, thing, [this, thing, reply]() {
TvDevice *tv = m_tvList.key(thing);
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if(status != 200) {
qCWarning(dcLgSmartTv) << "Pair TV request error:" << status << reply->errorString();
tv->setPaired(false);
} else {
qCDebug(dcLgSmartTv) << "Paired TV successfully.";
tv->setPaired(true);
refreshTv(thing);
}
});
QPair<QNetworkRequest, QByteArray> request = TvDevice::createPairingRequest(host, port, eventHandlerPort, key);
QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
connect(reply, &QNetworkReply::finished, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, thing, [this, thing, reply]() {
TvDevice *tv = m_tvList.key(thing);
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if(status != 200) {
qCWarning(dcLgSmartTv) << "Pair TV request error:" << status << reply->errorString();
tv->setPaired(false);
} else {
qCDebug(dcLgSmartTv) << "Paired TV successfully.";
tv->setPaired(true);
loadAppList(thing);
refreshTv(thing);
}
});
}
}
void IntegrationPluginLgSmartTv::unpairTvDevice(Thing *thing)
{
QHostAddress host = QHostAddress(thing->paramValue(lgSmartTvThingHostAddressParamTypeId).toString());
int port = thing->paramValue(lgSmartTvThingPortParamTypeId).toInt();
QPair<QNetworkRequest, QByteArray> request = TvDevice::createEndPairingRequest(host, port);
if (thing->thingClassId() == lgSmartTvThingClassId) {
QHostAddress host = QHostAddress(thing->paramValue(lgSmartTvThingHostAddressParamTypeId).toString());
quint16 port = thing->paramValue(lgSmartTvThingPortParamTypeId).toUInt();
quint16 eventHandlerPort = thing->paramValue(lgSmartTvThingEventHandlerPortParamTypeId).toUInt();
QPair<QNetworkRequest, QByteArray> request = TvDevice::createEndPairingRequest(host, port, eventHandlerPort);
QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [reply](){
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if(status != 200) {
qCWarning(dcLgSmartTv) << "End pairing TV (thing deleted) request error:" << status << reply->errorString();
} else {
qCDebug(dcLgSmartTv) << "End pairing TV (thing deleted) successfully.";
}
});
QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [reply](){
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if(status != 200) {
qCWarning(dcLgSmartTv) << "End pairing TV (thing deleted) request error:" << status << reply->errorString();
} else {
qCDebug(dcLgSmartTv) << "End pairing TV (thing deleted) successfully.";
}
});
}
}
void IntegrationPluginLgSmartTv::refreshTv(Thing *thing)
{
TvDevice *tv = m_tvList.key(thing);
// check volume information
QNetworkReply *volumeReply = hardwareManager()->networkManager()->get(tv->createVolumeInformationRequest());
connect(volumeReply, &QNetworkReply::finished, this, &IntegrationPluginLgSmartTv::onNetworkManagerReplyFinished);
m_volumeInfoRequests.insert(volumeReply, thing);
QNetworkReply *reply = hardwareManager()->networkManager()->get(tv->createVolumeInformationRequest());
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [=](){
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (status != 200) {
tv->setReachable(false);
qCWarning(dcLgSmartTv) << "Refresh tv information request error:" << status << reply->errorString();
return;
}
if (!tv->reachable()) {
loadAppList(thing);
}
tv->setReachable(true);
tv->onVolumeInformationUpdate(reply->readAll());
// QByteArray data = reply->readAll();
// qCDebug(dcLgSmartTv()) << "Refresh info received:" << data << qUtf8Printable(TvDevice::printXmlData(data));
// QNetworkReply *reply = hardwareManager()->networkManager()->get(tv->createAppListRequest());
// connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
// connect(reply, &QNetworkReply::finished, this, [reply, tv](){
// int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
// if (status != 200) {
// tv->setReachable(false);
// qCWarning(dcLgSmartTv) << "Refresh tv information request error:" << status << reply->errorString();
// return;
// }
// tv->setReachable(true);
// QByteArray data = reply->readAll();
// qCDebug(dcLgSmartTv()) << "Refresh info received:" << data << qUtf8Printable(TvDevice::printXmlData(data));
// tv->setReachable(true);
// tv->onVolumeInformationUpdate(reply->readAll());
// // Check channel information
// QNetworkReply *channelReply = hardwareManager()->networkManager()->get(tv->createChannelInformationRequest());
// connect(channelReply, &QNetworkReply::finished, this, &IntegrationPluginLgSmartTv::onNetworkManagerReplyFinished);
// m_channelInfoRequests.insert(channelReply, thing);
// });
});
}
void IntegrationPluginLgSmartTv::loadAppList(Thing *thing)
{
TvDevice *tv = m_tvList.key(thing);
tv->clearAppList();
// Note:: loading all apps (type 1) does not work, so load type 2 (premium) and typ 3 (myApps) sequentially
QNetworkReply *reply = hardwareManager()->networkManager()->get(tv->createAppListCountRequest(2));
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [=](){
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (status != 200) {
tv->setReachable(false);
qCWarning(dcLgSmartTv) << "Get app count request error:" << status << reply->errorString();
return;
}
tv->setReachable(true);
QByteArray data = reply->readAll();
int appCount = 0;
QXmlStreamReader xml(data);
while (!xml.atEnd() && !xml.hasError()) {
xml.readNext();
if (xml.name() == "number") {
appCount = QVariant(xml.readElementText()).toInt();
}
}
QNetworkReply *reply = hardwareManager()->networkManager()->get(tv->createAppListRequest(2, appCount));
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [=](){
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (status != 200) {
tv->setReachable(false);
qCWarning(dcLgSmartTv) << "Get app list request error:" << status << reply->errorString();
return;
}
QByteArray data = reply->readAll();
// Parse app list
QVariantList appList;
QXmlStreamReader xml(qUtf8Printable(QString::fromLatin1(data)));
while (xml.readNextStartElement() && !xml.atEnd()) {
if (xml.name() == "dataList") {
while (xml.readNextStartElement()) {
QVariantMap appMap;
if (xml.name() == "data") {
qCDebug(dcLgSmartTv()) << "--------------------";
while (xml.readNextStartElement()) {
qCDebug(dcLgSmartTv()) << "-->" << xml.name() << xml.readElementText();
if (xml.name() == "name") {
appMap.insert("name", xml.readElementText());
} else if (xml.name() == "icon") {
appMap.insert("icon", xml.readElementText());
} if (xml.name() == "cpid") {
appMap.insert("cpid", xml.readElementText());
}
}
}
appList.append(appMap);
}
}
}
qCDebug(dcLgSmartTv()) << "Adding app list" << appList;
tv->addAppList(appList);
QNetworkReply *reply = hardwareManager()->networkManager()->get(tv->createAppListCountRequest(3));
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [=](){
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (status != 200) {
tv->setReachable(false);
qCWarning(dcLgSmartTv) << "Get app count request error:" << status << reply->errorString();
return;
}
QByteArray data = reply->readAll();
int appCount = 0;
QXmlStreamReader xml(data);
while (!xml.atEnd() && !xml.hasError()) {
xml.readNext();
if (xml.name() == "number") {
appCount = QVariant(xml.readElementText()).toInt();
}
}
QNetworkReply *reply = hardwareManager()->networkManager()->get(tv->createAppListRequest(3, appCount));
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [reply, tv](){
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (status != 200) {
tv->setReachable(false);
qCWarning(dcLgSmartTv) << "Refresh tv information request error:" << status << reply->errorString();
return;
}
QByteArray data = reply->readAll();
// Parse app list
QVariantList appList;
QXmlStreamReader xml(QString::fromLatin1(data));
while (xml.readNextStartElement() && !xml.atEnd()) {
if (xml.name() == "dataList") {
while (xml.readNextStartElement()) {
if (xml.name() == "data") {
QVariantMap appMap;
while (xml.readNextStartElement()) {
if (xml.name() == "name") {
appMap.insert("name", xml.readElementText());
} else if (xml.name() == "icon") {
appMap.insert("icon", xml.readElementText());
} if (xml.name() == "auid") {
appMap.insert("auid", xml.readElementText());
}
}
qCDebug(dcLgSmartTv()) << appMap;
appList.append(appMap);
}
}
}
}
qCDebug(dcLgSmartTv()) << "Adding app list" << appList;
tv->addAppList(appList);
});
});
});
});
}
void IntegrationPluginLgSmartTv::onPluginTimer()
@ -426,7 +647,7 @@ void IntegrationPluginLgSmartTv::onNetworkManagerReplyFinished()
if (m_volumeInfoRequests.keys().contains(reply)) {
Thing *thing = m_volumeInfoRequests.take(reply);
TvDevice *tv = m_tvList.key(thing);
if(status != 200) {
if (status != 200) {
tv->setReachable(false);
qCWarning(dcLgSmartTv) << "Volume information request error:" << status << reply->errorString();
if (status == 401) {
@ -445,7 +666,7 @@ void IntegrationPluginLgSmartTv::onNetworkManagerReplyFinished()
} else if (m_channelInfoRequests.keys().contains(reply)) {
Thing *thing = m_channelInfoRequests.take(reply);
TvDevice *tv = m_tvList.key(thing);
if(status != 200) {
if (status != 200) {
tv->setReachable(false);
qCWarning(dcLgSmartTv) << "Channel information request error:" << status << reply->errorString();
if (status == 401) {
@ -466,8 +687,8 @@ void IntegrationPluginLgSmartTv::stateChanged()
thing->setStateValue(lgSmartTvConnectedStateTypeId, tvDevice->reachable());
thing->setStateValue(lgSmartTvTv3DModeStateTypeId, tvDevice->is3DMode());
thing->setStateValue(lgSmartTvTvVolumeLevelStateTypeId, tvDevice->volumeLevel());
thing->setStateValue(lgSmartTvTvMuteStateTypeId, tvDevice->mute());
thing->setStateValue(lgSmartTvVolumeLevelStateTypeId, tvDevice->volumeLevel());
thing->setStateValue(lgSmartTvMuteStateTypeId, tvDevice->mute());
thing->setStateValue(lgSmartTvTvChannelTypeStateTypeId, tvDevice->channelType());
thing->setStateValue(lgSmartTvTvChannelNameStateTypeId, tvDevice->channelName());
thing->setStateValue(lgSmartTvTvChannelNumberStateTypeId, tvDevice->channelNumber());

View File

@ -61,6 +61,9 @@ public:
void executeAction(ThingActionInfo *info) override;
void browseThing(BrowseResult *result) override;
void browserItem(BrowserItemResult *result) override;
private:
PluginTimer *m_pluginTimer = nullptr;
QHash<TvDevice *, Thing *> m_tvList;
@ -76,6 +79,8 @@ private:
void unpairTvDevice(Thing *thing);
void refreshTv(Thing *thing);
void loadAppList(Thing *thing);
private slots:
void onPluginTimer();
void onNetworkManagerReplyFinished();

View File

@ -14,41 +14,51 @@
"displayName": "LG Smart Tv",
"createMethods": [ "discovery" ],
"setupMethod": "displayPin",
"interfaces": ["connectable"],
"browsable": true,
"interfaces": ["mediacontroller", "navigationpad", "volumecontroller", "connectable"],
"paramTypes": [
{
"id": "797a1915-cb85-400a-9c1a-db09c73b54d5",
"name": "name",
"displayName": "name",
"displayName": "Name",
"type": "QString",
"inputType": "TextLine"
},
{
"id": "0deec372-b955-49d2-9906-fc1a603dd637",
"name": "model",
"displayName": "model",
"displayName": "Model",
"type": "QString",
"inputType": "TextLine"
},
{
"id": "0af152bb-8844-44d8-afee-d08bb0e28558",
"name": "uuid",
"displayName": "uuid",
"displayName": "UUID",
"type": "QString",
"inputType": "TextLine"
},
{
"id": "9710c07c-0725-4099-bf73-2513efbc0872",
"name": "hostAddress",
"displayName": "host address",
"displayName": "Host address",
"type": "QString",
"inputType": "IPv4Address"
"inputType": "IPv4Address",
"defaultValue": "127.0.0.1"
},
{
"id": "442abce0-16f9-4628-9534-e98e7f1c0a51",
"name": "port",
"displayName": "port",
"type": "int"
"displayName": "Port",
"type": "uint",
"defaultValue": 8080
},
{
"id": "2904dc2d-d734-4893-ab69-222cd7a5d147",
"name": "eventHandlerPort",
"displayName": "Event handler port",
"type": "uint",
"defaultValue": 9000
}
],
"stateTypes": [
@ -69,21 +79,24 @@
"defaultValue": false
},
{
"id": "07d39a6e-7eab-42d0-851d-9f3bcd3bbb57",
"name": "tvVolumeLevel",
"displayName": "volume",
"displayNameEvent": "Volume changed",
"type": "int",
"unit": "Percentage",
"defaultValue": 0
"id": "e1ead9b0-2db5-41bd-aedb-b78a99235dde",
"name": "mute",
"displayName": "Mute",
"displayNameEvent": "Mute changed",
"displayNameAction": "Set mute",
"type": "bool",
"defaultValue": false,
"writable": true
},
{
"id": "a6ac9061-3de7-403a-a646-790ca5d73764",
"name": "tvMute",
"displayNameEvent": "mute changed",
"displayName": "mute",
"type": "bool",
"defaultValue": false
"id": "597f9e07-108e-4345-80ab-1842bcb40666",
"displayName": "Volume",
"displayNameEvent": "Volume changed",
"maxValue": 100,
"minValue": 0,
"name": "volumeLevel",
"type": "int",
"defaultValue": 10
},
{
"id": "84c86670-77c7-4fc6-9e23-abca066e76aa",
@ -137,73 +150,87 @@
"actionTypes": [
{
"id": "ac5d7dcd-dfe8-4a94-9ab9-21b3f804b39e",
"name": "commandVolumeUp",
"displayName": "volume up"
"name": "increaseVolume",
"displayName": "Volume +"
},
{
"id": "62b17bec-f461-4ffa-93d1-67a9430d55e1",
"name": "commandVolumeDown",
"displayName": "volume down"
},
{
"id": "1aa9d7f0-0f66-4b90-bb72-f6b7b2118221",
"name": "commandMute",
"displayName": "mute"
},
{
"id": "b7e31999-ba67-443d-8e5c-ec104af987bd",
"name": "commandUnmute",
"displayName": "unmute"
"name": "decreaseVolume",
"displayName": "Volume -"
},
{
"id": "acd1f6a0-2cfa-4665-9607-cf94245ec5a3",
"name": "commandChannelUp",
"displayName": "channel up"
"displayName": "Channel up"
},
{
"id": "6ea66772-0e6d-40b1-978c-a01fb53871dd",
"name": "commandChannelDown",
"displayName": "channel down"
"displayName": "Channel down"
},
{
"id": "cbe41134-ff11-4916-815b-3ac289c64090",
"name": "commandPowerOff",
"displayName": "power off"
"displayName": "Power off"
},
{
"id": "57c483b4-4ddf-4470-828c-8d8767e7a923",
"name": "commandArrowUp",
"displayName": "arrow up"
"id": "193f80bf-3c9a-47a4-b54e-7cecf0bf0f45",
"name": "navigate",
"displayName": "Navigate",
"paramTypes": [
{
"id": "93861dac-0c24-4a3b-903d-d1be44eae611",
"name": "to",
"displayName": "to",
"type": "QString",
"allowedValues": [
"up",
"down",
"left",
"right",
"enter",
"back",
"menu",
"info",
"home"
]
}
]
},
{
"id": "614cf1af-5cf7-4bb2-885c-4414078d8899",
"name": "commandArrowDown",
"displayName": "arrow down"
"id": "2c1e07e1-198a-45f4-9c58-2c5e31290262",
"name": "skipBack",
"displayName": "skip back"
},
{
"id": "916394dd-7833-4875-8d7a-49d7d24ceeb2",
"name": "commandArrowLeft",
"displayName": "arrow left"
"id": "db984804-acad-4905-bc89-9ebcdf64478e",
"name": "fastRewind",
"displayName": "rewind"
},
{
"id": "01e3df1e-638b-4e14-ba85-660267766062",
"name": "commandArrowRight",
"displayName": "arrow right"
"id": "590c0f43-be86-4692-9b89-13730e71bed4",
"name": "stop",
"displayName": "stop"
},
{
"id": "257dfa59-0d38-4e18-a3fc-213809fdb12f",
"name": "commandOk",
"displayName": "OK"
"id": "17f5c9d4-e060-4daa-a039-3cd7eb6ef8bf",
"name": "play",
"displayName": "play"
},
{
"id": "ce4184b3-6b8e-4fc3-a4cb-7b8ec72f2ce9",
"name": "commandBack",
"displayName": "back"
"id": "6360fe9e-00c6-4d32-bcfc-69b5b5d85f8b",
"name": "pause",
"displayName": "pause"
},
{
"id": "33f941c1-f5fc-4449-b6e3-93eafca493e0",
"name": "commandHome",
"displayName": "home"
"id": "48c7afb6-8252-4c11-aff9-e4e4954b2d30",
"name": "fastForward",
"displayName": "fastForward"
},
{
"id": "353ad30a-25b7-4ae9-b47b-057bf101f8d1",
"name": "skipNext",
"displayName": "skipNext"
},
{
"id": "9a6e5111-95d3-49ac-8056-249e704b1509",
@ -215,11 +242,6 @@
"name": "commandExit",
"displayName": "exit"
},
{
"id": "9c1290d5-3135-4124-a576-fc7522cffdcf",
"name": "commandInfo",
"displayName": "info"
},
{
"id": "47d65cac-fe75-4c36-9dee-9862c1c1130e",
"name": "commandMyApps",
@ -238,7 +260,7 @@
"displayName": "LG WebOS Smart Tv",
"createMethods": [ "Discovery" ],
"setupMethod": "PushButton",
"interfaces": [ "extendedvolumecontroller", "mediacontroller", "connectable" ],
"interfaces": [ "volumecontroller", "mediacontroller", "connectable" ],
"paramTypes": [
{
"id": "adebba0c-ac7b-4b6a-8668-307f44dbe5bf",
@ -288,6 +310,16 @@
}
],
"actionTypes": [
{
"id": "ff814f79-5c79-4346-94fa-4da1a6f95c82",
"displayName": "Volume +",
"name": "increaseVolume"
},
{
"id": "360326c4-c6c3-4304-b539-aca08f711c8a",
"displayName": "Volume -",
"name": "decreaseVolume"
},
{
"id": "35cdb587-956a-4310-8e4c-85fdc240943e",
"displayName": "Skip back",

View File

@ -31,20 +31,32 @@
#include "tvdevice.h"
#include "extern-plugininfo.h"
TvDevice::TvDevice(const QHostAddress &hostAddress, const int &port, QObject *parent) :
TvDevice::TvDevice(const QHostAddress &hostAddress, quint16 port, quint16 eventHandlerPort, QObject *parent) :
QObject(parent),
m_hostAddress(hostAddress),
m_port(port),
m_paired(false),
m_reachable(false),
m_is3DMode(false),
m_mute(false),
m_volumeLevel(-1),
m_inputSourceIndex(-1),
m_channelNumber(-1)
m_eventHandlerPort(eventHandlerPort)
{
m_eventHandler = new TvEventHandler(hostAddress, port, this);
m_eventHandler = new TvEventHandler(hostAddress, eventHandlerPort, this);
connect(m_eventHandler, &TvEventHandler::eventOccured, this, &TvDevice::eventOccured);
// Init FS
m_virtualFs = new VirtualFsNode(BrowserItem());
// Apps
BrowserItem item = BrowserItem("apps", tr("Apps"), true);
item.setDescription(tr(""));
item.setIcon(BrowserItem::BrowserIconApplication);
VirtualFsNode *apps = new VirtualFsNode(item);
m_virtualFs->addChild(apps);
// Channels
item = BrowserItem("channels", tr("Channels"), true);
item.setDescription(tr(""));
item.setIcon(BrowserItem::BrowserIconVideo);
VirtualFsNode *channels = new VirtualFsNode(item);
m_virtualFs->addChild(channels);
}
void TvDevice::setKey(const QString &key)
@ -57,26 +69,21 @@ QString TvDevice::key() const
return m_key;
}
void TvDevice::setHostAddress(const QHostAddress &hostAddress)
{
m_hostAddress = hostAddress;
}
QHostAddress TvDevice::hostAddress() const
{
return m_hostAddress;
}
void TvDevice::setPort(const int &port)
{
m_port = port;
}
int TvDevice::port() const
quint16 TvDevice::port() const
{
return m_port;
}
quint16 TvDevice::eventHandlerPort() const
{
return m_eventHandlerPort;
}
void TvDevice::setUuid(const QString &uuid)
{
m_uuid = uuid;
@ -91,7 +98,7 @@ void TvDevice::setPaired(const bool &paired)
{
if (m_paired != paired) {
m_paired = paired;
stateChanged();
emit stateChanged();
}
}
@ -159,7 +166,7 @@ QString TvDevice::inputSourceLabelName() const
return m_inputSourceLabel;
}
QPair<QNetworkRequest, QByteArray> TvDevice::createDisplayKeyRequest(const QHostAddress &host, const int &port)
QPair<QNetworkRequest, QByteArray> TvDevice::createDisplayKeyRequest(const QHostAddress &host, quint16 port)
{
QString urlString = "http://" + host.toString() + ":" + QString::number(port) + "/udap/api/pairing";
QNetworkRequest request;
@ -171,7 +178,7 @@ QPair<QNetworkRequest, QByteArray> TvDevice::createDisplayKeyRequest(const QHost
return QPair<QNetworkRequest, QByteArray>(request, data);
}
QPair<QNetworkRequest, QByteArray> TvDevice::createPairingRequest(const QHostAddress &host, const int &port, const QString &key)
QPair<QNetworkRequest, QByteArray> TvDevice::createPairingRequest(const QHostAddress &host, quint16 port, quint16 eventHandlerPort, const QString &key)
{
QString urlString = "http://" + host.toString() + ":" + QString::number(port) + "/udap/api/pairing";
QNetworkRequest request;
@ -179,23 +186,12 @@ QPair<QNetworkRequest, QByteArray> TvDevice::createPairingRequest(const QHostAdd
request.setHeader(QNetworkRequest::ContentTypeHeader,QVariant("text/xml; charset=utf-8"));
request.setHeader(QNetworkRequest::UserAgentHeader,QVariant("UDAP/2.0 nymea"));
QByteArray data = "<?xml version=\"1.0\" encoding=\"utf-8\"?><envelope><api type=\"pairing\"><name>hello</name><value>" + key.toUtf8() + "</value><port>8080</port></api></envelope>";
QByteArray data = "<?xml version=\"1.0\" encoding=\"utf-8\"?><envelope><api type=\"pairing\"><name>hello</name><value>" +
key.toUtf8() + "</value><port>" + QByteArray::number(eventHandlerPort) + "</port></api></envelope>";
return QPair<QNetworkRequest, QByteArray>(request, data);
}
QPair<QNetworkRequest, QByteArray> TvDevice::createEndPairingRequest(const QUrl &url)
{
QNetworkRequest request;
request.setUrl(url);
request.setHeader(QNetworkRequest::ContentTypeHeader,QVariant("text/xml; charset=utf-8"));
request.setHeader(QNetworkRequest::UserAgentHeader,QVariant("UDAP/2.0 nymea"));
request.setRawHeader("Connection", "Close");
QByteArray data = "<?xml version=\"1.0\" encoding=\"utf-8\"?><envelope><api type=\"pairing\"><name>byebye</name><port>8080</port></api></envelope>";
return QPair<QNetworkRequest, QByteArray>(request, data);
}
QPair<QNetworkRequest, QByteArray> TvDevice::createEndPairingRequest(const QHostAddress &host, const int &port)
QPair<QNetworkRequest, QByteArray> TvDevice::createEndPairingRequest(const QHostAddress &host, quint16 port, quint16 eventHandlerPort)
{
QString urlString = "http://" + host.toString() + ":" + QString::number(port) + "/udap/api/pairing";
QNetworkRequest request;
@ -204,11 +200,11 @@ QPair<QNetworkRequest, QByteArray> TvDevice::createEndPairingRequest(const QHost
request.setHeader(QNetworkRequest::UserAgentHeader,QVariant("UDAP/2.0 nymea"));
request.setRawHeader("Connection", "Close");
QByteArray data = "<?xml version=\"1.0\" encoding=\"utf-8\"?><envelope><api type=\"pairing\"><name>byebye</name><port>8080</port></api></envelope>";
QByteArray data = "<?xml version=\"1.0\" encoding=\"utf-8\"?><envelope><api type=\"pairing\"><name>byebye</name><port>" + QByteArray::number(eventHandlerPort) + "</port></api></envelope>";
return QPair<QNetworkRequest, QByteArray>(request, data);
}
QPair<QNetworkRequest, QByteArray> TvDevice::createEventRequest(const QHostAddress &host, const int &port)
QPair<QNetworkRequest, QByteArray> TvDevice::createEventRequest(const QHostAddress &host, quint16 port, quint16 eventHandlerPort)
{
QString urlString = "http://" + host.toString() + ":" + QString::number(port) + "/udap/api/event";
QNetworkRequest request;
@ -217,16 +213,54 @@ QPair<QNetworkRequest, QByteArray> TvDevice::createEventRequest(const QHostAddre
request.setHeader(QNetworkRequest::UserAgentHeader,QVariant("UDAP/2.0 nymea"));
request.setRawHeader("Connection", "Close");
QByteArray data = "<?xml version=\"1.0\" encoding=\"utf-8\"?><envelope><api type=\"event\"><name>byebye</name><port>8080</port></api></envelope>";
QByteArray data = "<?xml version=\"1.0\" encoding=\"utf-8\"?><envelope><api type=\"event\"><name>byebye</name><port>" + QByteArray::number(eventHandlerPort) + "</port></api></envelope>";
return QPair<QNetworkRequest, QByteArray>(request, data);
}
QUrl TvDevice::buildDisplayImageUrl() const
{
return QUrl("http://" + m_hostAddress.toString() + ":" + QString::number(m_port) + "/udap/api/data?target=screen_image");
}
QNetworkRequest TvDevice::createVolumeInformationRequest()
{
QString urlString = "http://" + hostAddress().toString() + ":" + QString::number(port()) + "/udap/api/data?target=volume_info";
QNetworkRequest request;
request.setUrl(QUrl(urlString));
request.setHeader(QNetworkRequest::ContentTypeHeader,QVariant("text/xml"));
request.setHeader(QNetworkRequest::ContentTypeHeader,QVariant("text/xml; charset=utf-8"));
request.setHeader(QNetworkRequest::UserAgentHeader,QVariant("UDAP/2.0"));
request.setRawHeader("Connection", "Close");
return request;
}
QNetworkRequest TvDevice::createChannelListRequest()
{
QString urlString = "http://" + hostAddress().toString() + ":" + QString::number(port()) + "/udap/api/data?target=channel_list";
QNetworkRequest request;
request.setUrl(QUrl(urlString));
request.setHeader(QNetworkRequest::ContentTypeHeader,QVariant("text/xml; charset=utf-8"));
request.setHeader(QNetworkRequest::UserAgentHeader,QVariant("UDAP/2.0"));
request.setRawHeader("Connection", "Close");
return request;
}
QNetworkRequest TvDevice::createAppListCountRequest(int type)
{
QString urlString = "http://" + hostAddress().toString() + ":" + QString::number(port()) + "/udap/api/data?target=appnum_get&type=" + QString::number(type);
QNetworkRequest request;
request.setUrl(QUrl(urlString));
request.setHeader(QNetworkRequest::ContentTypeHeader,QVariant("text/xml; charset=utf-8"));
request.setHeader(QNetworkRequest::UserAgentHeader,QVariant("UDAP/2.0"));
request.setRawHeader("Connection", "Close");
return request;
}
QNetworkRequest TvDevice::createAppListRequest(int type, int appCount)
{
QString urlString = "http://" + hostAddress().toString() + ":" + QString::number(port()) + "/udap/api/data?target=applist_get&type=" + QString::number(type) + "&index=1&number=" + QString::number(appCount);
QNetworkRequest request;
request.setUrl(QUrl(urlString));
request.setHeader(QNetworkRequest::ContentTypeHeader,QVariant("text/xml; charset=utf-8"));
request.setHeader(QNetworkRequest::UserAgentHeader,QVariant("UDAP/2.0"));
request.setRawHeader("Connection", "Close");
return request;
@ -237,7 +271,7 @@ QNetworkRequest TvDevice::createChannelInformationRequest()
QString urlString = "http://" + hostAddress().toString() + ":" + QString::number(port()) + "/udap/api/data?target=cur_channel";
QNetworkRequest request;
request.setUrl(QUrl(urlString));
request.setHeader(QNetworkRequest::ContentTypeHeader,QVariant("text/xml"));
request.setHeader(QNetworkRequest::ContentTypeHeader,QVariant("text/xml; charset=utf-8"));
request.setHeader(QNetworkRequest::UserAgentHeader,QVariant("UDAP/2.0"));
request.setRawHeader("Connection", "Close");
return request;
@ -262,13 +296,13 @@ void TvDevice::onVolumeInformationUpdate(const QByteArray &data)
{
//qCDebug(dcLgSmartTv()) << printXmlData(data);
QXmlStreamReader xml(data);
while(!xml.atEnd() && !xml.hasError()) {
while (!xml.atEnd() && !xml.hasError()) {
xml.readNext();
if(xml.name() == "mute") {
if (xml.name() == "mute") {
m_mute = QVariant(xml.readElementText()).toBool();
}
if(xml.name() == "level") {
if (xml.name() == "level") {
m_volumeLevel = QVariant(xml.readElementText()).toInt();
}
}
@ -280,56 +314,121 @@ void TvDevice::onChannelInformationUpdate(const QByteArray &data)
//qCDebug(dcLgSmartTv()) << printXmlData(data);
QXmlStreamReader xml(data);
while(!xml.atEnd() && !xml.hasError()) {
while (!xml.atEnd() && !xml.hasError()) {
xml.readNext();
if(xml.name() == "chtype") {
if (xml.name() == "chtype") {
m_channelType = xml.readElementText();
}
if(xml.name() == "major") {
if (xml.name() == "major") {
m_channelNumber = QVariant(xml.readElementText()).toInt();
}
if(xml.name() == "chname") {
if (xml.name() == "chname") {
m_channelName = xml.readElementText();
}
if(xml.name() == "progName") {
if (xml.name() == "progName") {
m_programName = xml.readElementText();
}
if(xml.name() == "inputSourceIdx") {
if (xml.name() == "inputSourceIdx") {
m_inputSourceIndex = QVariant(xml.readElementText()).toInt();
}
if(xml.name() == "labelName") {
if (xml.name() == "labelName") {
m_inputSourceLabel = xml.readElementText();
}
}
emit stateChanged();
}
void TvDevice::onAppListUpdated(const QByteArray &appList)
{
Q_UNUSED(appList)
}
QString TvDevice::printXmlData(const QByteArray &data)
{
QString xmlOut;
QXmlStreamReader reader(data);
QXmlStreamWriter writer(&xmlOut);
writer.setAutoFormatting(true);
while (!reader.atEnd()) {
reader.readNext();
if(!reader.isWhitespace()) {
writer.writeCurrentToken(reader);
}
}
if(reader.hasError()) {
qCWarning(dcLgSmartTv()) << "error reading XML thing information:" << reader.errorString();
if (reader.hasError()) {
qCWarning(dcLgSmartTv()) << "Error reading XML thing information:" << reader.errorString();
}
return xmlOut;
}
void TvDevice::browse(BrowseResult *result)
{
qCDebug(dcLgSmartTv()) << "Browse" << result->itemId();
VirtualFsNode *node = m_virtualFs->findNode(result->itemId());
if (node) {
if (result->itemId().isEmpty()) {
foreach (VirtualFsNode *child, node->childs) {
result->addItem(child->item);
}
result->finish(Thing::ThingErrorNoError);
return;
}
if (result->itemId().startsWith("apps")) {
qCDebug(dcLgSmartTv()) << m_appList;
// Build the app list
foreach (const QVariant &appVariant, m_appList) {
QVariantMap appMap = appVariant.toMap();
qCDebug(dcLgSmartTv()) << "Entry:" << appMap;
BrowserItem item("apps:" + appMap.value("name").toString(), appMap.value("name").toString());
item.setExecutable(true);
item.setIcon(BrowserItem::BrowserIconApplication);
result->addItem(item);
}
result->finish(Thing::ThingErrorNoError);
} else if (result->itemId().startsWith("channels")) {
// Build the channel list
}
}
}
void TvDevice::browserItem(BrowserItemResult *result)
{
qCDebug(dcLgSmartTv()) << "Browse item" << result->itemId();
}
int TvDevice::launchBrowserItem(const QString &itemId)
{
qCDebug(dcLgSmartTv()) << "Launch browse item" << itemId;
// TODO: switch channel or launch app
return 0;
}
void TvDevice::clearAppList()
{
m_appList.clear();
}
void TvDevice::addAppList(const QVariantList &appList)
{
foreach (const QVariant &appVariant, appList) {
m_appList.append(appVariant);
}
}
void TvDevice::eventOccured(const QByteArray &data)
{
qCDebug(dcLgSmartTv()) << "Event handler data received" << printXmlData(data);
qCDebug(dcLgSmartTv()) << "Event handler data received" << qUtf8Printable(printXmlData(data));
// if we got a channel changed event...
if(data.contains("ChannelChanged")) {
if (data.contains("ChannelChanged")) {
onChannelInformationUpdate(data);
return;
}
@ -338,7 +437,7 @@ void TvDevice::eventOccured(const QByteArray &data)
// if the tv suspends, it will send a byebye message, which means
// the pairing will be closed.
if(data.contains("api type=\"pairing\"") && data.contains("byebye")) {
if (data.contains("api type=\"pairing\"") && data.contains("byebye")) {
qCDebug(dcLgSmartTv()) << "Ended pairing (host)";
setPaired(false);
setReachable(false);
@ -347,14 +446,12 @@ void TvDevice::eventOccured(const QByteArray &data)
// check if this is a 3DMode changed event
QXmlStreamReader xml(data);
while(!xml.atEnd() && !xml.hasError()) {
while (!xml.atEnd() && !xml.hasError()) {
xml.readNext();
if(xml.name() == "name") {
if(xml.readElementText() == "3DMode") {
if (xml.name() == "name") {
if (xml.readElementText() == "3DMode") {
xml.readNext();
if(xml.name() == "value") {
if (xml.name() == "value") {
m_is3DMode = QVariant(xml.readElementText()).toBool();
}
}

View File

@ -50,7 +50,7 @@ class TvDevice : public QObject
{
Q_OBJECT
public:
explicit TvDevice(const QHostAddress &hostAddress, const int &port, QObject *parent = 0);
explicit TvDevice(const QHostAddress &hostAddress, quint16 port, quint16 eventHandlerPort, QObject *parent = 0);
enum RemoteKey{
Power = 1,
@ -125,11 +125,9 @@ public:
void setKey(const QString &key);
QString key() const;
void setHostAddress(const QHostAddress &hostAddress);
QHostAddress hostAddress() const;
void setPort(const int &port);
int port() const;
quint16 port() const;
quint16 eventHandlerPort() const;
void setUuid(const QString &uuid);
QString uuid() const;
@ -152,41 +150,78 @@ public:
QString inputSourceLabelName() const;
// other methods
static QPair<QNetworkRequest, QByteArray> createDisplayKeyRequest(const QHostAddress &host, const int &port);
static QPair<QNetworkRequest, QByteArray> createPairingRequest(const QHostAddress &host, const int &port, const QString &key);
static QPair<QNetworkRequest, QByteArray> createEndPairingRequest(const QUrl &url);
static QPair<QNetworkRequest, QByteArray> createEndPairingRequest(const QHostAddress &host, const int &port);
static QPair<QNetworkRequest, QByteArray> createEventRequest(const QHostAddress &host, const int &port);
static QPair<QNetworkRequest, QByteArray> createDisplayKeyRequest(const QHostAddress &host, quint16 port);
static QPair<QNetworkRequest, QByteArray> createPairingRequest(const QHostAddress &host, quint16 port, quint16 eventHandlerPort, const QString &key);
static QPair<QNetworkRequest, QByteArray> createEndPairingRequest(const QHostAddress &host, quint16 port, quint16 eventHandlerPort);
static QPair<QNetworkRequest, QByteArray> createEventRequest(const QHostAddress &host, quint16 port, quint16 eventHandlerPort);
QPair<QNetworkRequest, QByteArray> createPressButtonRequest(const TvDevice::RemoteKey &key);
QUrl buildDisplayImageUrl() const;
QNetworkRequest createVolumeInformationRequest();
QNetworkRequest createChannelInformationRequest();
QNetworkRequest createChannelListRequest();
QNetworkRequest createAppListCountRequest(int type);
QNetworkRequest createAppListRequest(int type, int appCount);
void onVolumeInformationUpdate(const QByteArray &data);
void onChannelInformationUpdate(const QByteArray &data);
void onAppListUpdated(const QByteArray &appList);
static QString printXmlData(const QByteArray &data);
void browse(BrowseResult *result);
void browserItem(BrowserItemResult *result);
int launchBrowserItem(const QString &itemId);
void clearAppList();
void addAppList(const QVariantList &appList);
private:
TvEventHandler *m_eventHandler;
QHostAddress m_hostAddress;
int m_port;
quint16 m_port = 0;
quint16 m_eventHandlerPort = 0;
QString m_uuid;
QString m_key;
QVariantList m_appList;
// States
bool m_paired;
bool m_reachable;
bool m_is3DMode;
bool m_mute;
int m_volumeLevel;
int m_inputSourceIndex;
int m_channelNumber;
bool m_paired = false;
bool m_reachable = false;
bool m_is3DMode = false;
bool m_mute = false;
int m_volumeLevel = 20;
int m_inputSourceIndex = -1;
int m_channelNumber = -1;
QString m_channelType;
QString m_channelName;
QString m_programName;
QString m_inputSourceLabel;
QString printXmlData(const QByteArray &data);
class VirtualFsNode {
public:
VirtualFsNode(const BrowserItem &item):item(item) {}
~VirtualFsNode() { qDeleteAll(childs); }
BrowserItem item;
QList<VirtualFsNode*> childs;
QString getMethod;
QVariantMap getParams;
void addChild(VirtualFsNode* child) {childs.append(child); }
VirtualFsNode *findNode(const QString &id) {
if (item.id() == id) return this;
foreach (VirtualFsNode *child, childs) {
VirtualFsNode *node = child->findNode(id);
if (node) return node;
}
return nullptr;
}
};
VirtualFsNode* m_virtualFs = nullptr;
signals:
void stateChanged();

View File

@ -31,20 +31,41 @@
#include "tveventhandler.h"
#include "extern-plugininfo.h"
TvEventHandler::TvEventHandler(const QHostAddress &host, const int &port, QObject *parent) :
TvEventHandler::TvEventHandler(const QHostAddress &host, quint16 eventHandlerPort, QObject *parent) :
QTcpServer(parent),
m_host(host),
m_port(port),
m_port(eventHandlerPort),
m_expectingData(false)
{
listen(QHostAddress::AnyIPv4, m_port);
if (!listen(QHostAddress::AnyIPv4, m_port)) {
qCWarning(dcLgSmartTv()) << "EventHandler: Could not listen for events on" << m_port;
} else {
qCDebug(dcLgSmartTv()) << "EventHandler: listening on port" << m_port;
}
}
uint TvEventHandler::getFreePort()
{
uint port = 9000;
QTcpServer testServer;
for (int i = 0; i < 100; i++) {
if (testServer.listen(QHostAddress::AnyIPv4, port)) {
qCDebug(dcLgSmartTv()) << "EventHandler: found free port" << port;
testServer.close();
return port;
} else {
qCDebug(dcLgSmartTv()) << "EventHandler: port not free" << port << "picking next one...";
}
}
return port;
}
void TvEventHandler::incomingConnection(qintptr socket)
{
QTcpSocket* tcpSocket = new QTcpSocket(this);
tcpSocket->setSocketDescriptor(socket);
qCDebug(dcLgSmartTv()) << "Event handler -> incoming connection" << tcpSocket->peerAddress().toString() << tcpSocket->peerName();
qCDebug(dcLgSmartTv()) << "EventHandler: incoming connection" << tcpSocket->peerAddress().toString() << tcpSocket->peerName();
connect(tcpSocket, &QTcpSocket::readyRead, this, &TvEventHandler::readClient);
connect(tcpSocket, &QTcpSocket::disconnected, this, &TvEventHandler::onDisconnected);
}
@ -53,22 +74,22 @@ void TvEventHandler::readClient()
{
QTcpSocket* socket = (QTcpSocket*)sender();
// reject everything, except the tv
// Reject everything, except data from the TV
if(socket->peerAddress() != m_host){
socket->close();
socket->deleteLater();
qCWarning(dcLgSmartTv()) << "Event handler -> rejecting connection from " << socket->peerAddress().toString();
qCWarning(dcLgSmartTv()) << "EventHandler: rejecting connection from " << socket->peerAddress().toString();
return;
}
// the tv sends first the header (POST /udap/api/.... HTTP/1.1)
// in the scond package the tv sends the information (xml format)
while(!socket->atEnd()){
while (!socket->atEnd()){
QByteArray data = socket->readAll();
// check if we got information
if(data.startsWith("<?xml") && m_expectingData){
if (data.startsWith("<?xml") && m_expectingData){
m_expectingData = false;
// Answere with OK
@ -86,7 +107,7 @@ void TvEventHandler::readClient()
if (data.startsWith("POST") && !m_expectingData) {
m_expectingData = true;
QStringList tokens = QString(data).split(QRegExp("[ \r\n][ \r\n]*"));
qCDebug(dcLgSmartTv()) << "event handler -> event occured" << "http://" << m_host.toString() << ":" << m_port << tokens[1];
qCDebug(dcLgSmartTv()) << "EventHandler: event occured" << "http://" << m_host.toString() << ":" << m_port << tokens[1];
}
}
}
@ -94,7 +115,7 @@ void TvEventHandler::readClient()
void TvEventHandler::onDisconnected()
{
QTcpSocket* socket = (QTcpSocket*)sender();
qCDebug(dcLgSmartTv()) << "event handler -> client disconnected" << socket->peerAddress();
qCDebug(dcLgSmartTv()) << "EventHandler: client disconnected" << socket->peerAddress();
socket->deleteLater();
}

View File

@ -45,14 +45,16 @@ class TvEventHandler : public QTcpServer
{
Q_OBJECT
public:
explicit TvEventHandler(const QHostAddress &host, const int &port = 8080, QObject *parent = 0);
explicit TvEventHandler(const QHostAddress &host, quint16 eventHandlerPort = 9000, QObject *parent = 0);
static uint getFreePort();
protected:
void incomingConnection(qintptr socket) override;
private:
QHostAddress m_host;
int m_port;
quint16 m_port;
bool m_expectingData;
signals: