mirror of https://github.com/nymea/nymea.git
293 lines
10 KiB
C++
293 lines
10 KiB
C++
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* *
|
|
* This file is part of guh. *
|
|
* *
|
|
* Guh is free software: you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation, version 2 of the License. *
|
|
* *
|
|
* Guh is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
|
|
* *
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
#include "tvdiscovery.h"
|
|
|
|
TvDiscovery::TvDiscovery(QObject *parent) :
|
|
QUdpSocket(parent)
|
|
{
|
|
m_timeout = new QTimer(this);
|
|
m_timeout->setSingleShot(true);
|
|
connect(m_timeout,SIGNAL(timeout()),this,SLOT(discoverTimeout()));
|
|
|
|
m_manager = new QNetworkAccessManager(this);
|
|
connect(m_manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(replyFinished(QNetworkReply*)));
|
|
|
|
m_port = 1900;
|
|
m_host = QHostAddress("239.255.255.250");
|
|
setSocketOption(QAbstractSocket::MulticastTtlOption,QVariant(1));
|
|
setSocketOption(QAbstractSocket::MulticastLoopbackOption,QVariant(1));
|
|
bind(QHostAddress::AnyIPv4,m_port,QUdpSocket::ShareAddress);
|
|
joinMulticastGroup(m_host);
|
|
connect(this,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(error(QAbstractSocket::SocketError)));
|
|
connect(this,SIGNAL(readyRead()),this,SLOT(readData()));
|
|
}
|
|
|
|
bool TvDiscovery::checkXmlData(QByteArray data)
|
|
{
|
|
QByteArray xmlOut;
|
|
QXmlStreamReader reader(data);
|
|
QXmlStreamWriter writer(&xmlOut);
|
|
writer.setAutoFormatting(true);
|
|
|
|
while (!reader.atEnd()) {
|
|
reader.readNext();
|
|
if (!reader.isWhitespace()) {
|
|
writer.writeCurrentToken(reader);
|
|
}
|
|
}
|
|
if(reader.hasError()){
|
|
qDebug() << "ERROR reading XML device information: " << reader.errorString();
|
|
qDebug() << "--------------------------------------------";
|
|
return false;
|
|
}
|
|
m_deviceInformationData = xmlOut;
|
|
return true;
|
|
}
|
|
|
|
QString TvDiscovery::printXmlData(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()){
|
|
qDebug() << "ERROR reading XML device information: " << reader.errorString();
|
|
qDebug() << "--------------------------------------------";
|
|
}
|
|
return xmlOut;
|
|
}
|
|
|
|
|
|
void TvDiscovery::error(QAbstractSocket::SocketError error)
|
|
{
|
|
qWarning() << errorString() << error;
|
|
}
|
|
|
|
void TvDiscovery::readData()
|
|
{
|
|
QByteArray data;
|
|
QHostAddress sender;
|
|
quint16 udpPort;
|
|
|
|
// read the answere from the multicast
|
|
while (hasPendingDatagrams()) {
|
|
data.resize(pendingDatagramSize());
|
|
readDatagram(data.data(), data.size(), &sender, &udpPort);
|
|
}
|
|
|
|
if(data.size() > 0){
|
|
if(data.contains("HTTP/1.1 200 OK")){
|
|
const QStringList lines = QString(data).split("\r\n");
|
|
|
|
QUrl location;
|
|
QString uuid;
|
|
QString server;
|
|
|
|
foreach( const QString& line, lines){
|
|
int separatorIndex = line.indexOf(':');
|
|
QString key = line.left(separatorIndex).toUpper();
|
|
QString value = line.mid(separatorIndex+1).trimmed();
|
|
|
|
// get location
|
|
if(key.contains("LOCATION")){
|
|
location = QUrl(value);
|
|
}
|
|
|
|
// get server info
|
|
if(key.contains("SERVER")){
|
|
// check if it is a LG Smart Tv with UDAP/2.0 protocoll
|
|
if(value.contains("UDAP/2.0")){
|
|
server = value;
|
|
}
|
|
}
|
|
|
|
// get uuid
|
|
if(key.contains("USN")){
|
|
int startIndex = value.indexOf(":");
|
|
int endIndex = value.indexOf("::");
|
|
uuid = value.mid(startIndex +1 ,(endIndex - startIndex)-1);
|
|
}
|
|
|
|
if(!location.isEmpty() && !uuid.isEmpty() && !server.isEmpty()){
|
|
foreach (TvDevice *device, m_tvList) {
|
|
if(device->uuid() == uuid){
|
|
return;
|
|
}
|
|
}
|
|
TvDevice *device = new TvDevice(this);
|
|
device->setLocation(location);
|
|
device->setHostAddress(sender);
|
|
device->setUuid(uuid);
|
|
|
|
m_tvList.append(device);
|
|
requestDeviceInformation(device);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void TvDiscovery::discoverTimeout()
|
|
{
|
|
emit discoveryDone(m_tvList);
|
|
}
|
|
|
|
void TvDiscovery::requestDeviceInformation(TvDevice *device)
|
|
{
|
|
|
|
QNetworkRequest deviceRequest;
|
|
deviceRequest.setUrl(device->location());
|
|
deviceRequest.setHeader(QNetworkRequest::ContentTypeHeader,QVariant("text/xml"));
|
|
deviceRequest.setHeader(QNetworkRequest::UserAgentHeader,QVariant("UDAP/2.0"));
|
|
|
|
m_deviceInformationReplay = m_manager->get(deviceRequest);
|
|
}
|
|
|
|
void TvDiscovery::replyFinished(QNetworkReply *reply)
|
|
{
|
|
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
|
QByteArray data;
|
|
|
|
switch (status) {
|
|
case(200):
|
|
data = reply->readAll();
|
|
if(checkXmlData(data)){
|
|
parseDeviceInformation(data);
|
|
}
|
|
break;
|
|
case(400):
|
|
qDebug() << "ERROR: 400 Bad request. The event format is not valid or it has an incorrect value.";
|
|
qDebug() << "--------------------------------------------";
|
|
return;
|
|
case(401):
|
|
qDebug() << "ERROR: 401 Unauthorized. An event is sent when a Host and a Controller are not paired.";
|
|
qDebug() << "--------------------------------------------";
|
|
return;
|
|
case(404):
|
|
qDebug() << "ERROR: 404 Not Found. The POST path of an event is incorrect.";
|
|
qDebug() << "--------------------------------------------";
|
|
return;
|
|
case(500):
|
|
qDebug() << "ERROR: 500 Internal Server Error. Event Execution Failure.";
|
|
qDebug() << "--------------------------------------------";
|
|
return;
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
void TvDiscovery::parseDeviceInformation(QByteArray data)
|
|
{
|
|
|
|
QXmlStreamReader xml(data);
|
|
|
|
QString name;
|
|
QString uuid;
|
|
QString modelName;
|
|
QString deviceType;
|
|
QString manufacturer;
|
|
int port;
|
|
|
|
qDebug() << printXmlData(data);
|
|
|
|
while(!xml.atEnd() && !xml.hasError()){
|
|
xml.readNext();
|
|
|
|
if(xml.isStartDocument()){
|
|
continue;
|
|
}
|
|
if(xml.isStartElement()){
|
|
if(xml.name() == "envelope"){
|
|
continue;
|
|
}
|
|
//check if we have device part of message
|
|
if(xml.name() == "device"){
|
|
// seems to be device information
|
|
while(!xml.atEnd()){
|
|
if(xml.name() == "deviceType" && xml.isStartElement()){
|
|
deviceType = xml.readElementText();
|
|
}
|
|
if(xml.name() == "modelName" && xml.isStartElement()){
|
|
modelName = xml.readElementText();
|
|
}
|
|
if(xml.name() == "friendlyName" && xml.isStartElement()){
|
|
name = xml.readElementText();
|
|
}
|
|
if(xml.name() == "manufacturer" && xml.isStartElement()){
|
|
manufacturer = xml.readElementText();
|
|
}
|
|
if(xml.name() == "uuid" && xml.isStartElement()){
|
|
uuid = xml.readElementText();
|
|
}
|
|
//check if we have port part of message
|
|
if(xml.name() == "port"){
|
|
port = xml.readElementText().toInt();
|
|
}
|
|
|
|
xml.readNext();
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
foreach (TvDevice *device, m_tvList) {
|
|
// find our device with this uuid
|
|
if(device->uuid() == uuid){
|
|
device->setName(name);
|
|
device->setModelName(modelName);
|
|
device->setDeviceType(deviceType);
|
|
device->setManufacturer(manufacturer);
|
|
device->setPort(port);
|
|
|
|
qDebug() << "--> fetched TV information...";
|
|
qDebug() << "name: " << device->name();
|
|
qDebug() << "model name: " << device->modelName();
|
|
qDebug() << "device type: " << device->deviceType();
|
|
qDebug() << "manufacturer: " << device->manufacturer();
|
|
qDebug() << "address: " << device->hostAddress().toString();
|
|
qDebug() << "port: " << device->port();
|
|
qDebug() << "location: " << device->location().toString();
|
|
qDebug() << "uuid: " << device->uuid();
|
|
qDebug() << "--------------------------------------------";
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void TvDiscovery::discover(int timeout)
|
|
{
|
|
QString searchMessage("M-SEARCH * HTTP/1.1\r\n"
|
|
"HOST:239.255.255.250:1900\r\n"
|
|
"MAN:\"ssdp:discover\"\r\n"
|
|
"MX:2\r\n"
|
|
"ST:udap:rootservice\r\n"
|
|
"USER-AGENT: UDAP/2.0 \r\n\r\n");
|
|
|
|
m_tvList.clear();
|
|
writeDatagram(searchMessage.toUtf8(),m_host,m_port);
|
|
m_timeout->start(timeout);
|
|
}
|