mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
LED Device Features, Fixes and Refactoring (Resubmit PR855) (#875)
* Refactor LedDevices - Initial version * Small renamings * Add WLED as own device * Lpd8806 Remove open() method * remove dependency on Qt 5.10 * Lpd8806 Remove open() method * Update WS281x * Update WS2812SPI * Add writeBlack for WLED powerOff * WLED remove extra bracket * Allow different Nanoleaf panel numbering sequence (Feature req.#827) * build(deps): bump websocket-extensions from 0.1.3 to 0.1.4 in /docs (#826) * Bumps [websocket-extensions](https://github.com/faye/websocket-extensions-node) from 0.1.3 to 0.1.4. - [Release notes](https://github.com/faye/websocket-extensions-node/releases) - [Changelog](https://github.com/faye/websocket-extensions-node/blob/master/CHANGELOG.md) - [Commits](https://github.com/faye/websocket-extensions-node/compare/0.1.3...0.1.4) * Fix typos * Nanoleaf clean-up * Yeelight support, generalize wizard elements * Update Yeelight to handle quota in music mode * Yeelight extend rage for extraTimeDarkness for testing * Clean-up - Add commentary, Remove development debug statements * Fix brightnessSwitchOffOnMinimum typo and default value * Yeelight support restoreOriginalState, additional Fixes * WLED - Remove UDP-Port, as it is not configurable * Fix merging issue * Remove QHostAddress::operator=(const QString&)' is deprecated * Windows compile errors and (Qt 5.15 deprecation) warnings * Fix order includes * LedDeviceFile Support Qt5.7 and greater * Windows compatibility and other Fixes * Fix Qt Version compatability * Rs232 - Resolve portname from unix /dev/ style, fix DMX sub-type support * Disable WLED Wizard Button (until Wizard is available) * Yeelight updates * Add wrong log-type as per #505 * Fixes and Clean-up after clang-tidy report * Fix udpe131 not enabled for generated CID * Change timer into dynamic for Qt Thread-Affinity * Hue clean-up and diyHue workaround * Updates after review feedback by m-seker * Add "chrono" includes
This commit is contained in:
@@ -1,25 +1,40 @@
|
||||
#include <ssdp/SSDPDiscover.h>
|
||||
|
||||
#include <utils/QStringUtils.h>
|
||||
|
||||
// Qt includes
|
||||
#include <QUdpSocket>
|
||||
#include <QUrl>
|
||||
#include <QRegularExpression>
|
||||
#include <QJsonObject>
|
||||
#include <QtNetwork>
|
||||
|
||||
static const QHostAddress SSDP_ADDR("239.255.255.250");
|
||||
static const quint16 SSDP_PORT(1900);
|
||||
#include <iostream>
|
||||
|
||||
// Constants
|
||||
namespace {
|
||||
|
||||
// as per upnp spec 1.1, section 1.2.2.
|
||||
// TODO: Make IP and port below another #define and replace message below
|
||||
static const QString UPNP_DISCOVER_MESSAGE = "M-SEARCH * HTTP/1.1\r\n"
|
||||
"HOST: 239.255.255.250:1900\r\n"
|
||||
const QString UPNP_DISCOVER_MESSAGE = "M-SEARCH * HTTP/1.1\r\n"
|
||||
"HOST: %1:%2\r\n"
|
||||
"MAN: \"ssdp:discover\"\r\n"
|
||||
"MX: 1\r\n"
|
||||
"ST: %1\r\n"
|
||||
"MX: %3\r\n"
|
||||
"ST: %4\r\n"
|
||||
"\r\n";
|
||||
} //End of constants
|
||||
|
||||
SSDPDiscover::SSDPDiscover(QObject* parent)
|
||||
: QObject(parent)
|
||||
, _log(Logger::getInstance("SSDPDISCOVER"))
|
||||
, _udpSocket(new QUdpSocket(this))
|
||||
, _log(Logger::getInstance("SSDPDISCOVER"))
|
||||
, _udpSocket(new QUdpSocket(this))
|
||||
, _ssdpAddr(DEFAULT_SEARCH_ADDRESS)
|
||||
, _ssdpPort(DEFAULT_SEARCH_PORT)
|
||||
, _ssdpMaxWaitResponseTime(1)
|
||||
, _ssdpTimeout(DEFAULT_SSDP_TIMEOUT.count())
|
||||
,_filter(DEFAULT_FILTER)
|
||||
,_filterHeader(DEFAULT_FILTER_HEADER)
|
||||
,_regExFilter(_filter)
|
||||
,_skipDupKeys(false)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -36,8 +51,9 @@ void SSDPDiscover::searchForService(const QString& st)
|
||||
|
||||
const QString SSDPDiscover::getFirstService(const searchType& type, const QString& st, const int& timeout_ms)
|
||||
{
|
||||
Debug(_log, "Search for Service [%s]", QSTRING_CSTR(st));
|
||||
_searchTarget = st;
|
||||
_services.clear();
|
||||
Debug(_log, "Search for Service [%s], address [%s], port [%d]", QSTRING_CSTR(_searchTarget), QSTRING_CSTR(_ssdpAddr.toString()), _ssdpPort);
|
||||
|
||||
// search
|
||||
sendSearch(_searchTarget);
|
||||
@@ -63,12 +79,7 @@ const QString SSDPDiscover::getFirstService(const searchType& type, const QStrin
|
||||
QString address;
|
||||
// parse request
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||
QStringList entries = data.split("\n", Qt::SkipEmptyParts);
|
||||
#else
|
||||
QStringList entries = data.split("\n", QString::SkipEmptyParts);
|
||||
#endif
|
||||
|
||||
QStringList entries = QStringUtils::split(data,"\n", QStringUtils::SplitBehavior::SkipEmptyParts);
|
||||
for(auto entry : entries)
|
||||
{
|
||||
// http header parse skip
|
||||
@@ -151,11 +162,7 @@ void SSDPDiscover::readPendingDatagrams()
|
||||
QString data(datagram);
|
||||
QMap<QString,QString> headers;
|
||||
// parse request
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||
QStringList entries = data.split("\n", Qt::SkipEmptyParts);
|
||||
#else
|
||||
QStringList entries = data.split("\n", QString::SkipEmptyParts);
|
||||
#endif
|
||||
QStringList entries = QStringUtils::split(data,"\n", QStringUtils::SplitBehavior::SkipEmptyParts);
|
||||
for(auto entry : entries)
|
||||
{
|
||||
// http header parse skip
|
||||
@@ -191,9 +198,184 @@ void SSDPDiscover::readPendingDatagrams()
|
||||
}
|
||||
}
|
||||
|
||||
int SSDPDiscover::discoverServices(const QString& searchTarget, const QString& key)
|
||||
{
|
||||
_searchTarget = searchTarget;
|
||||
int rc = -1;
|
||||
|
||||
Debug(_log, "Search for Service [%s], address [%s], port [%d]", QSTRING_CSTR(_searchTarget), QSTRING_CSTR(_ssdpAddr.toString()), _ssdpPort);
|
||||
|
||||
_services.clear();
|
||||
|
||||
// search
|
||||
sendSearch(_searchTarget);
|
||||
|
||||
if ( _udpSocket->waitForReadyRead( _ssdpTimeout ) )
|
||||
{
|
||||
while (_udpSocket->waitForReadyRead(500))
|
||||
{
|
||||
QByteArray datagram;
|
||||
while (_udpSocket->hasPendingDatagrams())
|
||||
{
|
||||
|
||||
datagram.resize(_udpSocket->pendingDatagramSize());
|
||||
QHostAddress sender;
|
||||
quint16 senderPort;
|
||||
|
||||
_udpSocket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);
|
||||
|
||||
QString data(datagram);
|
||||
|
||||
//Debug(_log, "_data: [%s]", QSTRING_CSTR(data));
|
||||
|
||||
QMap<QString,QString> headers;
|
||||
// parse request
|
||||
QStringList entries = QStringUtils::split(data,"\n", QStringUtils::SplitBehavior::SkipEmptyParts);
|
||||
for(auto entry : entries)
|
||||
{
|
||||
// http header parse skip
|
||||
if(entry.contains("HTTP/1.1"))
|
||||
continue;
|
||||
|
||||
// split into key:vale, be aware that value field may contain also a ":"
|
||||
entry = entry.simplified();
|
||||
int pos = entry.indexOf(":");
|
||||
if(pos == -1)
|
||||
continue;
|
||||
|
||||
headers[entry.left(pos).trimmed().toUpper()] = entry.mid(pos+1).trimmed();
|
||||
}
|
||||
|
||||
QRegularExpressionMatch match = _regExFilter.match(headers[_filterHeader]);
|
||||
if ( match.hasMatch() )
|
||||
{
|
||||
Debug(_log,"Found target [%s], plus record [%s] matches [%s:%s]", QSTRING_CSTR(_searchTarget), QSTRING_CSTR(headers[_filterHeader]), QSTRING_CSTR(_filterHeader), QSTRING_CSTR(_filter) );
|
||||
//Debug(_log, "_data: [%s]", QSTRING_CSTR(data));
|
||||
|
||||
QString mapKey = headers[key];
|
||||
|
||||
SSDPService service;
|
||||
service.cacheControl = headers["CACHE-CONTROL"];
|
||||
service.location = QUrl (headers["LOCATION"]);
|
||||
service.server = headers["SERVER"];
|
||||
service.searchTarget = headers["ST"];
|
||||
service.uniqueServiceName = headers["USN"];
|
||||
|
||||
headers.remove("CACHE-CONTROL");
|
||||
headers.remove("LOCATION");
|
||||
headers.remove("SERVER");
|
||||
headers.remove("ST");
|
||||
headers.remove("USN");
|
||||
|
||||
service.otherHeaders = headers;
|
||||
|
||||
if ( _skipDupKeys )
|
||||
{
|
||||
_services.insert(mapKey, service);
|
||||
}
|
||||
else
|
||||
{
|
||||
_services.insertMulti(mapKey, service);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_udpSocket->close();
|
||||
|
||||
if ( _services.empty() )
|
||||
{
|
||||
Debug(_log,"Search target [%s], no record(s) matching [%s:%s]", QSTRING_CSTR(_searchTarget), QSTRING_CSTR(_filterHeader), QSTRING_CSTR(_filter) );
|
||||
rc = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = _services.size();
|
||||
Debug(_log," [%d] service record(s) found", rc );
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
QJsonArray SSDPDiscover::getServicesDiscoveredJson()
|
||||
{
|
||||
QJsonArray result;
|
||||
|
||||
QMap<QString, SSDPService>::iterator i;
|
||||
for (i = _services.begin(); i != _services.end(); ++i)
|
||||
{
|
||||
//Debug(_log, "Device discovered at [%s]", QSTRING_CSTR( i.key() ));
|
||||
|
||||
QJsonObject obj;
|
||||
|
||||
obj.insert("id", i.key());
|
||||
|
||||
obj.insert("cache-control", i.value().cacheControl);
|
||||
obj.insert("location", i.value().location.toString());
|
||||
obj.insert("server", i.value().server);
|
||||
obj.insert("st", i.value().searchTarget);
|
||||
obj.insert("usn", i.value().uniqueServiceName);
|
||||
|
||||
QUrl url (i.value().location);
|
||||
obj.insert("ip", url.host());
|
||||
obj.insert("port", url.port());
|
||||
|
||||
QHostInfo hostInfo = QHostInfo::fromName(url.host());
|
||||
if (hostInfo.error() == QHostInfo::NoError )
|
||||
{
|
||||
QString hostname = hostInfo.hostName();
|
||||
//Seems that for Windows no local domain name is resolved
|
||||
if (!hostInfo.localDomainName().isEmpty() )
|
||||
{
|
||||
obj.insert("hostname", hostname.remove("."+hostInfo.localDomainName()));
|
||||
obj.insert("domain", hostInfo.localDomainName());
|
||||
}
|
||||
else
|
||||
{
|
||||
int domainPos = hostname.indexOf('.');
|
||||
obj.insert("hostname", hostname.left(domainPos));
|
||||
obj.insert("domain", hostname.mid(domainPos+1));
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject objOther;
|
||||
QMap <QString,QString>::iterator o;
|
||||
for (o = i.value().otherHeaders.begin(); o != i.value().otherHeaders.end(); ++o)
|
||||
{
|
||||
objOther.insert(o.key().toLower(), o.value());
|
||||
}
|
||||
obj.insert("other", objOther);
|
||||
|
||||
result << obj;
|
||||
}
|
||||
|
||||
//Debug(_log, "result: [%s]", QString(QJsonDocument(result).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
return result;
|
||||
}
|
||||
|
||||
void SSDPDiscover::sendSearch(const QString& st)
|
||||
{
|
||||
const QString msg = UPNP_DISCOVER_MESSAGE.arg(st);
|
||||
const QString msg = QString(UPNP_DISCOVER_MESSAGE).arg(_ssdpAddr.toString()).arg(_ssdpPort).arg(_ssdpMaxWaitResponseTime).arg(st);
|
||||
|
||||
_udpSocket->writeDatagram(msg.toUtf8(), QHostAddress(SSDP_ADDR), SSDP_PORT);
|
||||
//Debug(_log,"Search request: [%s]", QSTRING_CSTR(msg));
|
||||
_udpSocket->writeDatagram(msg.toUtf8(), _ssdpAddr, _ssdpPort);
|
||||
}
|
||||
|
||||
bool SSDPDiscover::setSearchFilter ( const QString& filter, const QString& filterHeader)
|
||||
{
|
||||
bool rc = true;
|
||||
QRegularExpression regEx( filter );
|
||||
if (!regEx.isValid()) {
|
||||
QString errorString = regEx.errorString();
|
||||
int errorOffset = regEx.patternErrorOffset();
|
||||
|
||||
Error(_log,"Filtering regular expression [%s] error [%d]:[%s]", QSTRING_CSTR(filter), errorOffset, QSTRING_CSTR(errorString) );
|
||||
rc = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_filter = filter;
|
||||
_filterHeader=filterHeader.toUpper();
|
||||
_regExFilter = regEx;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
// utils
|
||||
#include <utils/SysInfo.h>
|
||||
#include <utils/QStringUtils.h>
|
||||
|
||||
// Hyperion
|
||||
#include <HyperionConfig.h>
|
||||
@@ -137,11 +138,7 @@ void SSDPServer::readPendingDatagrams()
|
||||
QString data(datagram);
|
||||
QMap<QString,QString> headers;
|
||||
// parse request
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||
QStringList entries = data.split("\n", Qt::SkipEmptyParts);
|
||||
#else
|
||||
QStringList entries = data.split("\n", QString::SkipEmptyParts);
|
||||
#endif
|
||||
QStringList entries = QStringUtils::split(data,"\n", QStringUtils::SplitBehavior::SkipEmptyParts);
|
||||
for(auto entry : entries)
|
||||
{
|
||||
// http header parse skip
|
||||
|
Reference in New Issue
Block a user