Add basics for starting webos development and experimenting

This commit is contained in:
Simon Stürz 2020-06-30 16:41:05 +02:00 committed by Simon Stürz
parent 057251df08
commit 6d02d18df0
6 changed files with 499 additions and 139 deletions

View File

@ -54,43 +54,69 @@ void IntegrationPluginLgSmartTv::init()
void IntegrationPluginLgSmartTv::discoverThings(ThingDiscoveryInfo *info)
{
qCDebug(dcLgSmartTv()) << "Start discovering";
UpnpDiscoveryReply *reply = hardwareManager()->upnpDiscovery()->discoverDevices("udap:rootservice","UDAP/2.0");
if (info->thingClassId() == lgSmartTvThingClassId) {
// Clean up in any case when the reply finishes
connect(reply, &UpnpDiscoveryReply::finished, reply, &UpnpDiscoveryReply::deleteLater);
qCDebug(dcLgSmartTv()) << "Start discovering";
UpnpDiscoveryReply *reply = hardwareManager()->upnpDiscovery()->discoverDevices("udap:rootservice","UDAP/2.0");
// Connect reply to discovery info for rsults.
connect(reply, &UpnpDiscoveryReply::finished, info, [this, info, reply](){
// Clean up in any case when the reply finishes
connect(reply, &UpnpDiscoveryReply::finished, reply, &UpnpDiscoveryReply::deleteLater);
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."));
}
// Connect reply to discovery info for rsults.
connect(reply, &UpnpDiscoveryReply::finished, info, [this, info, reply](){
foreach (UpnpDeviceDescriptor upnpDeviceDescriptor, reply->deviceDescriptors()) {
if (!upnpDeviceDescriptor.friendlyName().contains("LG") || !upnpDeviceDescriptor.deviceType().contains("TV"))
continue;
qCDebug(dcLgSmartTv) << upnpDeviceDescriptor;
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());
descriptor.setParams(params);
foreach (Thing *existingThing, myThings()) {
if (existingThing->paramValue(lgSmartTvThingUuidParamTypeId).toString() == upnpDeviceDescriptor.uuid()) {
descriptor.setThingId(existingThing->id());
break;
}
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."));
}
info->addThingDescriptor(descriptor);
}
info->finish(Thing::ThingErrorNoError);
});
foreach (UpnpDeviceDescriptor upnpDeviceDescriptor, reply->deviceDescriptors()) {
if (!upnpDeviceDescriptor.friendlyName().contains("LG") || !upnpDeviceDescriptor.deviceType().contains("TV"))
continue;
qCDebug(dcLgSmartTv) << upnpDeviceDescriptor;
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());
descriptor.setParams(params);
foreach (Thing *existingThing, myThings()) {
if (existingThing->paramValue(lgSmartTvThingUuidParamTypeId).toString() == upnpDeviceDescriptor.uuid()) {
descriptor.setThingId(existingThing->id());
break;
}
}
info->addThingDescriptor(descriptor);
}
info->finish(Thing::ThingErrorNoError);
});
} else if (info->thingClassId() == webosTvThingClassId) {
qCDebug(dcLgSmartTv()) << "Start discovering Webos tv";
UpnpDiscoveryReply *reply = hardwareManager()->upnpDiscovery()->discoverDevices("");
connect(reply, &UpnpDiscoveryReply::finished, reply, &UpnpDiscoveryReply::deleteLater);
connect(reply, &UpnpDiscoveryReply::finished, info, [this, info, reply](){
Q_UNUSED(this)
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."));
}
foreach (UpnpDeviceDescriptor upnpDeviceDescriptor, reply->deviceDescriptors()) {
qCDebug(dcLgSmartTv) << upnpDeviceDescriptor;
// FIXME: check what are the propper upnp settings for discovering UPnP
}
info->finish(Thing::ThingErrorNoError);
});
}
}
void IntegrationPluginLgSmartTv::startPairing(ThingPairingInfo *info)
@ -158,49 +184,76 @@ void IntegrationPluginLgSmartTv::setupThing(ThingSetupInfo *info)
{
Thing *thing = info->thing();
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);
tvDevice->setUuid(thing->paramValue(lgSmartTvThingUuidParamTypeId).toString());
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);
tvDevice->setUuid(thing->paramValue(lgSmartTvThingUuidParamTypeId).toString());
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();
tvDevice->setKey(key);
tvDevice->setKey(key);
connect(tvDevice, &TvDevice::stateChanged, this, &IntegrationPluginLgSmartTv::stateChanged);
m_tvList.insert(tvDevice, thing);
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);
if (!m_pluginTimer) {
m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(5);
connect(m_pluginTimer, &PluginTimer::timeout, this, &IntegrationPluginLgSmartTv::onPluginTimer);
}
info->finish(Thing::ThingErrorNoError);
return;
}
info->finish(Thing::ThingErrorNoError);
if (thing->thingClassId() == webosTvThingClassId) {
QHostAddress hostAddress = QHostAddress(thing->paramValue(webosTvThingHostAddressParamTypeId).toString());
WebosConnection *webosConnection = new WebosConnection(this);
webosConnection->setHostAddress(hostAddress);
connect(webosConnection, &WebosConnection::connectedChanged, this, [thing](bool connected){
thing->setStateValue(webosTvConnectedStateTypeId, connected);
});
m_webosTvs.insert(webosConnection, thing);
webosConnection->connectTv();
}
}
void IntegrationPluginLgSmartTv::thingRemoved(Thing *thing)
{
if (!m_tvList.values().contains(thing))
if (thing->thingClassId() == lgSmartTvThingClassId) {
if (!m_tvList.values().contains(thing))
return;
TvDevice *tvDevice= m_tvList.key(thing);
qCDebug(dcLgSmartTv) << "Removing device" << thing->name();
unpairTvDevice(thing);
m_tvList.remove(tvDevice);
delete tvDevice;
if (m_tvList.isEmpty() && m_pluginTimer) {
hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
m_pluginTimer = nullptr;
}
return;
}
TvDevice *tvDevice= m_tvList.key(thing);
qCDebug(dcLgSmartTv) << "Removing device" << thing->name();
unpairTvDevice(thing);
m_tvList.remove(tvDevice);
delete tvDevice;
if (m_tvList.isEmpty() && m_pluginTimer) {
hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
m_pluginTimer = nullptr;
if (thing->thingClassId() == webosTvThingClassId) {
WebosConnection *connection = m_webosTvs.key(thing);
m_webosTvs.remove(connection);
delete connection;
}
}
void IntegrationPluginLgSmartTv::postSetupThing(Thing *thing)
{
pairTvDevice(thing);
if (thing->thingClassId() == lgSmartTvThingClassId) {
pairTvDevice(thing);
}
}
void IntegrationPluginLgSmartTv::executeAction(ThingActionInfo *info)
@ -208,89 +261,90 @@ void IntegrationPluginLgSmartTv::executeAction(ThingActionInfo *info)
Thing *thing = info->thing();
Action action = info->action();
TvDevice * tvDevice = m_tvList.key(thing);
if (thing->thingClassId() == lgSmartTvThingClassId) {
if (!tvDevice->reachable()) {
qCWarning(dcLgSmartTv) << "Device not reachable";
return info->finish(Thing::ThingErrorHardwareNotAvailable);
}
TvDevice * tvDevice = m_tvList.key(thing);
if (!tvDevice->reachable()) {
qCWarning(dcLgSmartTv) << "Device not reachable";
return info->finish(Thing::ThingErrorHardwareNotAvailable);
}
QNetworkReply *reply = nullptr;
QNetworkReply *reply = nullptr;
if (action.actionTypeId() == lgSmartTvCommandVolumeUpActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::VolUp);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandVolumeDownActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::VolDown);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandMuteActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Mute);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} 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) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::ChannelDown);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} 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);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandArrowDownActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Down);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandArrowLeftActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Left);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandArrowRightActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Right);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandOkActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Ok);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandBackActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Back);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandHomeActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Home);
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) {
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) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::MyApps);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandProgramListActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::ProgramList);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
}
if (action.actionTypeId() == lgSmartTvCommandVolumeUpActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::VolUp);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandVolumeDownActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::VolDown);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandMuteActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Mute);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} 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) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::ChannelDown);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} 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);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandArrowDownActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Down);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandArrowLeftActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Left);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandArrowRightActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Right);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandOkActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Ok);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandBackActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Back);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandHomeActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::Home);
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) {
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) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::MyApps);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
} else if(action.actionTypeId() == lgSmartTvCommandProgramListActionTypeId) {
QPair<QNetworkRequest, QByteArray> request = tvDevice->createPressButtonRequest(TvDevice::ProgramList);
reply = hardwareManager()->networkManager()->post(request.first, request.second);
}
if (!reply) {
Q_ASSERT_X(false, "DevicePluginLGSmartTV", "Unhandled action " + info->action().actionTypeId().toString().toUtf8());
info->finish(Thing::ThingErrorActionTypeNotFound);
return;
}
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) << "Action request error:" << status << reply->errorString();
info->finish(Thing::ThingErrorHardwareFailure);
if (!reply) {
Q_ASSERT_X(false, "DevicePluginLGSmartTV", "Unhandled action " + info->action().actionTypeId().toString().toUtf8());
info->finish(Thing::ThingErrorActionTypeNotFound);
return;
}
info->finish(Thing::ThingErrorNoError);
});
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) << "Action request error:" << status << reply->errorString();
info->finish(Thing::ThingErrorHardwareFailure);
return;
}
info->finish(Thing::ThingErrorNoError);
});
}
}
@ -306,12 +360,9 @@ void IntegrationPluginLgSmartTv::pairTvDevice(Thing *thing)
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();

View File

@ -33,6 +33,7 @@
#include "tvdevice.h"
#include "plugintimer.h"
#include "webosconnection.h"
#include "integrations/integrationplugin.h"
#include "network/upnp/upnpdevicedescriptor.h"
@ -65,6 +66,8 @@ private:
QHash<TvDevice *, Thing *> m_tvList;
QHash<QString, QString> m_tvKeys;
QHash<WebosConnection *, Thing *> m_webosTvs;
// update requests
QHash<QNetworkReply *, Thing *> m_volumeInfoRequests;
QHash<QNetworkReply *, Thing *> m_channelInfoRequests;

View File

@ -231,6 +231,89 @@
"displayName": "program list"
}
]
},
{
"id": "5203755d-1560-4d43-bc19-5375d37be8be",
"name": "webosTv",
"displayName": "LG WebOS Smart Tv",
"createMethods": [ "Discovery" ],
"setupMethod": "PushButton",
"interfaces": [ "extendedvolumecontroller", "mediacontroller", "connectable" ],
"paramTypes": [
{
"id": "adebba0c-ac7b-4b6a-8668-307f44dbe5bf",
"name": "hostAddress",
"displayName": "host address",
"type": "QString",
"inputType": "IPv4Address"
},
{
"id": "e367fa30-13c9-4380-b25c-489ac8bc98bb",
"name": "apiKey",
"displayName": "API key",
"type": "QString",
"inputType": "Password"
}
],
"stateTypes": [
{
"id": "a0304262-1054-4a18-ac9a-4462910c4133",
"name": "connected",
"displayName": "Reachable",
"displayNameEvent": "Reachable changed",
"type": "bool",
"defaultValue": false
},
{
"id": "8e49fe17-617d-4d2e-bf36-0a05e5687887",
"name": "mute",
"displayName": "Mute",
"displayNameEvent": "Mute changed",
"displayNameAction": "Set mute",
"type": "bool",
"defaultValue": false,
"writable": true
},
{
"id": "40551b26-a7b2-4d02-ab22-fa6a9c731c4f",
"displayName": "Volume",
"displayNameEvent": "Volume changed",
"displayNameAction": "Set volume",
"maxValue": 100,
"minValue": 0,
"name": "volume",
"type": "int",
"defaultValue": 10,
"writable": true
}
],
"actionTypes": [
{
"id": "35cdb587-956a-4310-8e4c-85fdc240943e",
"displayName": "Skip back",
"name": "skipBack"
},
{
"id": "04bb9eca-2ac5-4a49-a10b-1f10d612e394",
"displayName": "Stop",
"name": "stop"
},
{
"id": "5ded6518-c5f6-4043-9a4b-bf838c2029e4",
"displayName": "Play",
"name": "play"
},
{
"id": "9e6b2085-cef8-4c23-ae1a-c48cb359bccb",
"displayName": "Pause",
"name": "pause"
},
{
"id": "3a922eee-832f-4e8c-b13b-00fb788ac8be",
"displayName": "SkipNext",
"name": "skipNext"
}
]
}
]
}

View File

@ -2,16 +2,18 @@ include(../plugins.pri)
TARGET = $$qtLibraryTarget(nymea_integrationpluginlgsmarttv)
QT+= network xml
QT+= network xml websockets
SOURCES += \
integrationpluginlgsmarttv.cpp \
tvdevice.cpp \
tveventhandler.cpp
tveventhandler.cpp \
webosconnection.cpp
HEADERS += \
integrationpluginlgsmarttv.h \
tvdevice.h \
tveventhandler.h
tveventhandler.h \
webosconnection.h

View File

@ -0,0 +1,144 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; version 3. This project is distributed in the hope that
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "webosconnection.h"
#include "extern-plugininfo.h"
#include <QJsonDocument>
WebosConnection::WebosConnection(QObject *parent) :
QObject(parent)
{
m_websocket = new QWebSocket("", QWebSocketProtocol::VersionLatest, this);
connect(m_websocket, &QWebSocket::connected, this, &WebosConnection::onConnected);
connect(m_websocket, &QWebSocket::disconnected, this, &WebosConnection::onDisconnected);
connect(m_websocket, &QWebSocket::stateChanged, this, &WebosConnection::onStateChanged);
connect(m_websocket, &QWebSocket::textMessageReceived, this, &WebosConnection::onTextMessageReceived);
}
QHostAddress WebosConnection::hostAddress() const
{
return m_hostAddress;
}
void WebosConnection::setHostAddress(const QHostAddress &hostAddress)
{
m_hostAddress = hostAddress;
}
bool WebosConnection::connected() const
{
return m_websocket->state() == QAbstractSocket::ConnectedState;
}
void WebosConnection::sendRequest(const QVariantMap &request)
{
m_websocket->sendTextMessage(QJsonDocument::fromVariant(request).toJson());
m_id++;
}
void WebosConnection::getVolume()
{
qCDebug(dcLgSmartTv()) << "WebOS get volume";
// {"type":"request","id":"status_' + i +'","uri":"ssap://audio/getVolume"}
QVariantMap request;
request.insert("type", "request");
request.insert("id", QString("status_%1").arg(m_id));
request.insert("uri", "ssap://audio/getVolume");
// TODO: make async and match request with respone id
sendRequest(request);
}
void WebosConnection::onConnected()
{
qCDebug(dcLgSmartTv()) << "WebOS connected to" << m_hostAddress.toString();
emit connectedChanged(true);
m_id = 0;
// Register the client and init the current values
registerClient();
getVolume();
}
void WebosConnection::onDisconnected()
{
qCDebug(dcLgSmartTv()) << "WebOS disconnected from" << m_hostAddress.toString() << m_websocket->closeReason();
emit connectedChanged(false);
}
void WebosConnection::onStateChanged(const QAbstractSocket::SocketState &state)
{
qCDebug(dcLgSmartTv()) << "WebOS connection state changed" << state;
}
void WebosConnection::onTextMessageReceived(const QString &message)
{
qCDebug(dcLgSmartTv()) << "WebOS message received" << message;
// TODO: parse data and update stuff
}
void WebosConnection::registerClient()
{
// QVariantMap request;
// request.insert("type", "request");
// request.insert("id", QString("register_%1").arg(m_id));
// QVariantMap payload;
// payload.insert("forcePairing", false);
// payload.insert("pairingType", "PROMPT");
// QVariantMap manifest;
// manifest.insert("manifestVersion", 1);
// manifest.insert("appVersion", "1.1");
// payload.insert("manifest", manifest);
// request.insert("payload", payload);
// request.insert("uri", "ssap://audio/getVolume");
// Note: this is a static string copied from the internet
QByteArray requestData("{\"type\":\"register\",\"id\":\"register_0\",\"payload\":{\"forcePairing\":false,\"pairingType\":\"PROMPT\",\"manifest\":{\"manifestVersion\":1,\"appVersion\":\"1.1\",\"signed\":{\"created\":\"20140509\",\"appId\":\"com.lge.test\",\"vendorId\":\"com.lge\",\"localizedAppNames\":{\"\":\"LG Remote App\",\"ko-KR\":\"리모컨 앱\",\"zxx-XX\":\"ЛГ Rэмotэ AПП\"},\"localizedVendorNames\":{\"\":\"LG Electronics\"},\"permissions\":[\"TEST_SECURE\",\"CONTROL_INPUT_TEXT\",\"CONTROL_MOUSE_AND_KEYBOARD\",\"READ_INSTALLED_APPS\",\"READ_LGE_SDX\",\"READ_NOTIFICATIONS\",\"SEARCH\",\"WRITE_SETTINGS\",\"WRITE_NOTIFICATION_ALERT\",\"CONTROL_POWER\",\"READ_CURRENT_CHANNEL\",\"READ_RUNNING_APPS\",\"READ_UPDATE_INFO\",\"UPDATE_FROM_REMOTE_APP\",\"READ_LGE_TV_INPUT_EVENTS\",\"READ_TV_CURRENT_TIME\"],\"serial\":\"2f930e2d2cfe083771f68e4fe7bb07\"},\"permissions\":[\"LAUNCH\",\"LAUNCH_WEBAPP\",\"APP_TO_APP\",\"CLOSE\",\"TEST_OPEN\",\"TEST_PROTECTED\",\"CONTROL_AUDIO\",\"CONTROL_DISPLAY\",\"CONTROL_INPUT_JOYSTICK\",\"CONTROL_INPUT_MEDIA_RECORDING\",\"CONTROL_INPUT_MEDIA_PLAYBACK\",\"CONTROL_INPUT_TV\",\"CONTROL_POWER\",\"READ_APP_STATUS\",\"READ_CURRENT_CHANNEL\",\"READ_INPUT_DEVICE_LIST\",\"READ_NETWORK_STATE\",\"READ_RUNNING_APPS\",\"READ_TV_CHANNEL_LIST\",\"WRITE_NOTIFICATION_TOAST\",\"READ_POWER_STATE\",\"READ_COUNTRY_INFO\"],\"signatures\":[{\"signatureVersion\":1,\"signature\":\"eyJhbGdvcml0aG0iOiJSU0EtU0hBMjU2Iiwia2V5SWQiOiJ0ZXN0LXNpZ25pbmctY2VydCIsInNpZ25hdHVyZVZlcnNpb24iOjF9.hrVRgjCwXVvE2OOSpDZ58hR+59aFNwYDyjQgKk3auukd7pcegmE2CzPCa0bJ0ZsRAcKkCTJrWo5iDzNhMBWRyaMOv5zWSrthlf7G128qvIlpMT0YNY+n/FaOHE73uLrS/g7swl3/qH/BGFG2Hu4RlL48eb3lLKqTt2xKHdCs6Cd4RMfJPYnzgvI4BNrFUKsjkcu+WD4OO2A27Pq1n50cMchmcaXadJhGrOqH5YmHdOCj5NSHzJYrsW0HPlpuAx/ECMeIZYDh6RMqaFM2DXzdKX9NmmyqzJ3o/0lkk/N97gfVRLW5hA29yeAwaCViZNCP8iC9aO0q9fQojoa7NQnAtw==\"}]}}}");
sendRequest(QJsonDocument::fromJson(requestData).toVariant().toMap());
}
void WebosConnection::connectTv()
{
QUrl url;
url.setScheme("ws");
url.setHost(m_hostAddress.toString());
url.setPort(3000);
qCDebug(dcLgSmartTv()) << "Connecting to WebOS" << url.toString();
m_websocket->open(url);
}

View File

@ -0,0 +1,77 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; version 3. This project is distributed in the hope that
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef WEBOSCONNECTION_H
#define WEBOSCONNECTION_H
#include <QObject>
#include <QWebSocket>
class WebosConnection : public QObject
{
Q_OBJECT
public:
explicit WebosConnection(QObject *parent = nullptr);
QHostAddress hostAddress() const;
void setHostAddress(const QHostAddress &hostAddress);
QString apiKey() const;
void setApiKey(const QString &apiKey);
bool connected() const;
private:
QHostAddress m_hostAddress;
QWebSocket *m_websocket = nullptr;
QString m_apiKey;
int m_id = 0;
void sendRequest(const QVariantMap &request);
void getVolume();
signals:
void connectedChanged(bool connected);
private slots:
void onConnected();
void onDisconnected();
void onStateChanged(const QAbstractSocket::SocketState &state);
void onTextMessageReceived(const QString &message);
public slots:
void registerClient();
void connectTv();
};
#endif // WEBOSCONNECTION_H