diff --git a/assets/firmware/arduino/mega_pwm_led_driver.fzz b/assets/firmware/arduino/mega_pwm_led_driver.fzz deleted file mode 100644 index c1a97cbe..00000000 Binary files a/assets/firmware/arduino/mega_pwm_led_driver.fzz and /dev/null differ diff --git a/assets/webconfig/i18n/de.json b/assets/webconfig/i18n/de.json index 8b3c83fb..85a12a6e 100644 --- a/assets/webconfig/i18n/de.json +++ b/assets/webconfig/i18n/de.json @@ -21,6 +21,7 @@ "general_comp_BOBLIGHTSERVER" : "Boblight Server", "general_comp_GRABBER" : "Plattform Aufnahme", "general_comp_V4L" : "USB Aufnahme", + "general_comp_LEDDEVICE" : "LED Hardware", "general_col_red" : "rot", "general_col_green" : "grĂ¼n", "general_col_blue" : "blau", diff --git a/assets/webconfig/i18n/en.json b/assets/webconfig/i18n/en.json index 63857590..ec31e7ac 100644 --- a/assets/webconfig/i18n/en.json +++ b/assets/webconfig/i18n/en.json @@ -21,6 +21,7 @@ "general_comp_BOBLIGHTSERVER" : "Boblight Server", "general_comp_GRABBER" : "Platform Capture", "general_comp_V4L" : "USB Capture", + "general_comp_LEDDEVICE" : "LED device", "general_col_red" : "red", "general_col_green" : "green", "general_col_blue" : "blue", diff --git a/assets/webconfig/js/content_remote.js b/assets/webconfig/js/content_remote.js index 0f3a6132..bbbf3108 100644 --- a/assets/webconfig/js/content_remote.js +++ b/assets/webconfig/js/content_remote.js @@ -119,26 +119,24 @@ $(document).ready(function() { } if(ip) origin += '
'+$.i18n('remote_input_ip')+' '+ip+''; - if(compId == 10) + if(compId == "EFFECT") owner = $.i18n('remote_effects_label_effects')+' '+owner; - if(compId == 9) - owner = $.i18n('remote_color_label_color')+' '+'
'; - if(compId == 7) + if(compId == "COLOR") + owner = (owner == "Off") ? $.i18n('general_btn_off') : $.i18n('remote_color_label_color')+' '+'
'; + if(compId == "GRABBER") owner = $.i18n('general_comp_GRABBER')+': ('+owner+')'; - if(compId == 8) + if(compId == "V4L") owner = $.i18n('general_comp_V4L')+': ('+owner+')'; - if(compId == 6) + if(compId == "BOBLIGHTSERVER") owner = $.i18n('general_comp_BOBLIGHTSERVER'); - if(compId == 5) + if(compId == "UDPLISTENER") owner = $.i18n('general_comp_UDPLISTENER'); - if(owner == "Off") - owner = $.i18n('general_btn_off'); - if(duration && compId != 7 && compId != 11) + if(duration && compId != "GRABBER" && compId != "PROTOSERVER") owner += '
'+$.i18n('remote_input_duration')+' '+duration.toFixed(0)+$.i18n('edt_append_s')+''; var btn = ''; - if((compId == 10 || compId == 9) && priority != 254) + if((compId == "EFFECT" || compId == "COLOR") && priority < 254) btn += ''; if(btn_type != 'default') diff --git a/include/bonjour/bonjourrecord.h b/include/bonjour/bonjourrecord.h index ae2ebc00..63ed17ab 100755 --- a/include/bonjour/bonjourrecord.h +++ b/include/bonjour/bonjourrecord.h @@ -5,14 +5,14 @@ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. + this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. + derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF @@ -35,24 +35,34 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. class BonjourRecord { public: - BonjourRecord() {} - BonjourRecord(const QString &name, const QString ®Type, const QString &domain) - : serviceName(name), registeredType(regType), replyDomain(domain) - {} - BonjourRecord(const char *name, const char *regType, const char *domain) - { - serviceName = QString::fromUtf8(name); - registeredType = QString::fromUtf8(regType); - replyDomain = QString::fromUtf8(domain); - } - QString serviceName; - QString registeredType; - QString replyDomain; - bool operator==(const BonjourRecord &other) const { - return serviceName == other.serviceName - && registeredType == other.registeredType - && replyDomain == other.replyDomain; - } + BonjourRecord() : port(-1) {} + BonjourRecord(const QString &name, const QString ®Type, const QString &domain) + : serviceName(name) + , registeredType(regType) + , replyDomain(domain) + , port(-1) + {} + + BonjourRecord(const char *name, const char *regType, const char *domain) + : serviceName(QString::fromUtf8(name)) + , registeredType(QString::fromUtf8(regType)) + , replyDomain(QString::fromUtf8(domain)) + , port(-1) + { + } + + QString serviceName; + QString registeredType; + QString replyDomain; + QString hostName; + int port; + + bool operator==(const BonjourRecord &other) const + { + return serviceName == other.serviceName + && registeredType == other.registeredType + && replyDomain == other.replyDomain; + } }; Q_DECLARE_METATYPE(BonjourRecord) diff --git a/include/bonjour/bonjourservicebrowser.h b/include/bonjour/bonjourservicebrowser.h new file mode 100644 index 00000000..590747fd --- /dev/null +++ b/include/bonjour/bonjourservicebrowser.h @@ -0,0 +1,65 @@ +/* +Copyright (c) 2007, Trenton Schulz + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef BONJOURSERVICEBROWSER_H +#define BONJOURSERVICEBROWSER_H + +#include +#include +#include "bonjour/bonjourrecord.h" + + +class QSocketNotifier; +class BonjourServiceBrowser : public QObject +{ + Q_OBJECT +public: + BonjourServiceBrowser(QObject *parent = 0); + ~BonjourServiceBrowser(); + void browseForServiceType(const QString &serviceType); + inline QList currentRecords() const { return bonjourRecords; } + inline QString serviceType() const { return browsingType; } + +signals: + void currentBonjourRecordsChanged(const QList &list); + void error(DNSServiceErrorType err); + +private slots: + void bonjourSocketReadyRead(); + +private: + static void DNSSD_API bonjourBrowseReply(DNSServiceRef , DNSServiceFlags flags, quint32, + DNSServiceErrorType errorCode, const char *serviceName, + const char *regType, const char *replyDomain, void *context); + DNSServiceRef dnssref; + QSocketNotifier *bonjourSocket; + QList bonjourRecords; + QString browsingType; +}; + +#endif // BONJOURSERVICEBROWSER_H diff --git a/include/bonjour/bonjourserviceresolver.h b/include/bonjour/bonjourserviceresolver.h new file mode 100644 index 00000000..08ab3811 --- /dev/null +++ b/include/bonjour/bonjourserviceresolver.h @@ -0,0 +1,69 @@ +/* +Copyright (c) 2007, Trenton Schulz + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef BONJOURSERVICERESOLVER_H +#define BONJOURSERVICERESOLVER_H + +#include + + +#include + +class QSocketNotifier; +class QHostInfo; +class BonjourRecord; + +class BonjourServiceResolver : public QObject +{ + Q_OBJECT +public: + BonjourServiceResolver(QObject *parent); + ~BonjourServiceResolver(); + + bool resolveBonjourRecord(const BonjourRecord &record); + +signals: + void bonjourRecordResolved(const QHostInfo &hostInfo, int port); + void error(DNSServiceErrorType error); + +private slots: + void bonjourSocketReadyRead(); + void cleanupResolve(); + void finishConnect(const QHostInfo &hostInfo); + +private: + static void DNSSD_API bonjourResolveReply(DNSServiceRef sdRef, DNSServiceFlags flags, + quint32 interfaceIndex, DNSServiceErrorType errorCode, + const char *fullName, const char *hosttarget, quint16 port, + quint16 txtLen, const char *txtRecord, void *context); + DNSServiceRef dnssref; + QSocketNotifier *bonjourSocket; + int bonjourPort; +}; + +#endif // BONJOURSERVICERESOLVER_H diff --git a/include/hyperion/Hyperion.h b/include/hyperion/Hyperion.h index 3202b7af..973fef29 100644 --- a/include/hyperion/Hyperion.h +++ b/include/hyperion/Hyperion.h @@ -2,11 +2,12 @@ // stl includes #include -#include +#include // QT includes #include #include +#include #include #include #include @@ -33,6 +34,8 @@ // KodiVideoChecker includes #include +#include +#include // Forward class declaration class LedDevice; @@ -52,8 +55,8 @@ class Hyperion : public QObject public: /// Type definition of the info structure used by the priority muxer typedef PriorityMuxer::InputInfo InputInfo; - typedef std::map PriorityRegister; - + typedef QMap PriorityRegister; + typedef QMap BonjourRegister; /// /// RGB-Color channel enumeration /// @@ -257,6 +260,9 @@ public slots: /// sets the methode how image is maped to leds void setLedMappingType(int mappingType); + /// + Hyperion::BonjourRegister getHyperionSessions(); + public: static Hyperion *_hyperion; @@ -302,6 +308,10 @@ private slots: /// void update(); + void currentBonjourRecordsChanged(const QList &list); + void bonjourRecordResolved(const QHostInfo &hostInfo, int port); + void bonjourResolve(); + private: /// @@ -344,6 +354,7 @@ private: /// The timer for handling priority channel timeouts QTimer _timer; + QTimer _timerBonjourResolver; /// buffer for leds std::vector _ledBuffer; @@ -373,5 +384,9 @@ private: int _configVersionId; - hyperion::Components _prevCompId; + hyperion::Components _prevCompId; + BonjourServiceBrowser _bonjourBrowser; + BonjourServiceResolver _bonjourResolver; + BonjourRegister _hyperionSessions; + QString _bonjourCurrentServiceToResolve; }; diff --git a/include/leddevice/LedDevice.h b/include/leddevice/LedDevice.h index aae575eb..01af8188 100644 --- a/include/leddevice/LedDevice.h +++ b/include/leddevice/LedDevice.h @@ -19,6 +19,7 @@ #include #include #include +#include class LedDevice; @@ -58,6 +59,12 @@ public: static QJsonObject getLedDeviceSchemas(); static void setLedCount(int ledCount); static int getLedCount() { return _ledCount; } + + void setEnable(bool enable); + bool enabled() { return _enabled; }; + + inline bool componentState() { return enabled(); }; + protected: /// /// Writes the RGB-Color values to the leds. @@ -88,11 +95,13 @@ protected: /// e.g. Adalight device will switch off when it does not receive data at least every 15 seconds QTimer _refresh_timer; unsigned int _refresh_timer_interval; - + protected slots: /// Write the last data to the leds again int rewriteLeds(); private: std::vector _ledValues; + bool _componentRegistered; + bool _enabled; }; diff --git a/include/utils/Components.h b/include/utils/Components.h index 37165a6e..1754c2f2 100644 --- a/include/utils/Components.h +++ b/include/utils/Components.h @@ -19,7 +19,8 @@ enum Components COMP_V4L, COMP_COLOR, COMP_EFFECT, - COMP_PROTOSERVER + COMP_PROTOSERVER, + COMP_LEDDEVICE }; inline const char* componentToString(Components c) @@ -37,6 +38,7 @@ inline const char* componentToString(Components c) case COMP_COLOR: return "Solid color"; case COMP_EFFECT: return "Effect"; case COMP_PROTOSERVER: return "Proto Server"; + case COMP_LEDDEVICE: return "LED device"; default: return ""; } } @@ -56,6 +58,7 @@ inline const char* componentToIdString(Components c) case COMP_COLOR: return "COLOR"; case COMP_EFFECT: return "EFFECT"; case COMP_PROTOSERVER: return "PROTOSERVER"; + case COMP_LEDDEVICE: return "LEDDEVICE"; default: return ""; } } @@ -74,6 +77,7 @@ inline Components stringToComponent(QString component) if (component == "COLOR") return COMP_COLOR; if (component == "EFFECT") return COMP_EFFECT; if (component == "PROTOSERVER") return COMP_PROTOSERVER; + if (component == "LEDDEVICE") return COMP_LEDDEVICE; return COMP_INVALID; } diff --git a/include/utils/SysInfo.h b/include/utils/SysInfo.h index b6b97bb6..4ab17732 100644 --- a/include/utils/SysInfo.h +++ b/include/utils/SysInfo.h @@ -18,6 +18,7 @@ public: QString productVersion; QString prettyName; QString hostName; + QString domainName; }; ~SysInfo(); diff --git a/libsrc/bonjour/CMakeLists.txt b/libsrc/bonjour/CMakeLists.txt index 8d77aa76..e955bccc 100644 --- a/libsrc/bonjour/CMakeLists.txt +++ b/libsrc/bonjour/CMakeLists.txt @@ -6,6 +6,8 @@ set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/bonjour) # Group the headers that go through the MOC compiler set(Bonjour_QT_HEADERS ${CURRENT_HEADER_DIR}/bonjourserviceregister.h + ${CURRENT_HEADER_DIR}/bonjourservicebrowser.h + ${CURRENT_HEADER_DIR}/bonjourserviceresolver.h ) set(Bonjour_HEADERS @@ -13,13 +15,15 @@ set(Bonjour_HEADERS set(Bonjour_SOURCES ${CURRENT_SOURCE_DIR}/bonjourserviceregister.cpp + ${CURRENT_SOURCE_DIR}/bonjourservicebrowser.cpp + ${CURRENT_SOURCE_DIR}/bonjourserviceresolver.cpp ) -set(Bonjour_RESOURCES -) +#set(Bonjour_RESOURCES +#) qt5_wrap_cpp(Bonjour_HEADERS_MOC ${Bonjour_QT_HEADERS}) -qt5_add_resources(Bonjour_RESOURCES_RCC ${Bonjour_RESOURCES} OPTIONS "-no-compress") +#qt5_add_resources(Bonjour_RESOURCES_RCC ${Bonjour_RESOURCES} OPTIONS "-no-compress") add_library(bonjour ${Bonjour_HEADERS} diff --git a/libsrc/bonjour/bonjourservicebrowser.cpp b/libsrc/bonjour/bonjourservicebrowser.cpp new file mode 100644 index 00000000..34024db9 --- /dev/null +++ b/libsrc/bonjour/bonjourservicebrowser.cpp @@ -0,0 +1,109 @@ +/* +Copyright (c) 2007, Trenton Schulz + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "bonjour/bonjourservicebrowser.h" + +#include + +BonjourServiceBrowser::BonjourServiceBrowser(QObject *parent) + : QObject(parent) + , dnssref(0) + , bonjourSocket(0) +{ +} + +BonjourServiceBrowser::~BonjourServiceBrowser() +{ + if (dnssref) + { + DNSServiceRefDeallocate(dnssref); + dnssref = 0; + } +} + +void BonjourServiceBrowser::browseForServiceType(const QString &serviceType) +{ + DNSServiceErrorType err = DNSServiceBrowse(&dnssref, 0, 0, serviceType.toUtf8().constData(), 0, bonjourBrowseReply, this); + if (err != kDNSServiceErr_NoError) + { + emit error(err); + } + else + { + int sockfd = DNSServiceRefSockFD(dnssref); + if (sockfd == -1) + { + emit error(kDNSServiceErr_Invalid); + } + else + { + bonjourSocket = new QSocketNotifier(sockfd, QSocketNotifier::Read, this); + connect(bonjourSocket, SIGNAL(activated(int)), this, SLOT(bonjourSocketReadyRead())); + } + } +} + +void BonjourServiceBrowser::bonjourSocketReadyRead() +{ + DNSServiceErrorType err = DNSServiceProcessResult(dnssref); + if (err != kDNSServiceErr_NoError) + { + emit error(err); + } +} + +void BonjourServiceBrowser::bonjourBrowseReply(DNSServiceRef , DNSServiceFlags flags, + quint32 , DNSServiceErrorType errorCode, + const char *serviceName, const char *regType, + const char *replyDomain, void *context) +{ + BonjourServiceBrowser *serviceBrowser = static_cast(context); + if (errorCode != kDNSServiceErr_NoError) + { + emit serviceBrowser->error(errorCode); + } + else + { + BonjourRecord bonjourRecord(serviceName, regType, replyDomain); + if (flags & kDNSServiceFlagsAdd) + { + if (!serviceBrowser->bonjourRecords.contains(bonjourRecord)) + { + serviceBrowser->bonjourRecords.append(bonjourRecord); + } + } + else + { + serviceBrowser->bonjourRecords.removeAll(bonjourRecord); + } + if (!(flags & kDNSServiceFlagsMoreComing)) + { + emit serviceBrowser->currentBonjourRecordsChanged(serviceBrowser->bonjourRecords); + } + } +} diff --git a/libsrc/bonjour/bonjourserviceresolver.cpp b/libsrc/bonjour/bonjourserviceresolver.cpp new file mode 100644 index 00000000..217ae00f --- /dev/null +++ b/libsrc/bonjour/bonjourserviceresolver.cpp @@ -0,0 +1,122 @@ +/* +Copyright (c) 2007, Trenton Schulz + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#include "bonjour/bonjourrecord.h" +#include "bonjour/bonjourserviceresolver.h" + +BonjourServiceResolver::BonjourServiceResolver(QObject *parent) + : QObject(parent) + , dnssref(0) + , bonjourSocket(0) + , bonjourPort(-1) +{ +} + +BonjourServiceResolver::~BonjourServiceResolver() +{ + cleanupResolve(); +} + +void BonjourServiceResolver::cleanupResolve() +{ + if (dnssref) + { + DNSServiceRefDeallocate(dnssref); + dnssref = 0; + delete bonjourSocket; + bonjourPort = -1; + } +} + +bool BonjourServiceResolver::resolveBonjourRecord(const BonjourRecord &record) +{ + if (dnssref) + { + //qWarning("resolve in process, aborting"); + return false; + } + DNSServiceErrorType err = DNSServiceResolve(&dnssref, 0, 0, + record.serviceName.toUtf8().constData(), + record.registeredType.toUtf8().constData(), + record.replyDomain.toUtf8().constData(), + (DNSServiceResolveReply)bonjourResolveReply, this); + if (err != kDNSServiceErr_NoError) + { + emit error(err); + } + else + { + int sockfd = DNSServiceRefSockFD(dnssref); + if (sockfd == -1) + { + emit error(kDNSServiceErr_Invalid); + } + else + { + bonjourSocket = new QSocketNotifier(sockfd, QSocketNotifier::Read, this); + connect(bonjourSocket, SIGNAL(activated(int)), this, SLOT(bonjourSocketReadyRead())); + } + } + return true; +} + +void BonjourServiceResolver::bonjourSocketReadyRead() +{ + DNSServiceErrorType err = DNSServiceProcessResult(dnssref); + if (err != kDNSServiceErr_NoError) + emit error(err); +} + +void BonjourServiceResolver::bonjourResolveReply(DNSServiceRef sdRef, DNSServiceFlags , + quint32 , DNSServiceErrorType errorCode, + const char *, const char *hosttarget, quint16 port, + quint16 , const char *, void *context) +{ + BonjourServiceResolver *serviceResolver = static_cast(context); + if (errorCode != kDNSServiceErr_NoError) { + emit serviceResolver->error(errorCode); + return; + } +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + { + port = 0 | ((port & 0x00ff) << 8) | ((port & 0xff00) >> 8); + } +#endif + serviceResolver->bonjourPort = port; + + QHostInfo::lookupHost(QString::fromUtf8(hosttarget), serviceResolver, SLOT(finishConnect(const QHostInfo &))); +} + +void BonjourServiceResolver::finishConnect(const QHostInfo &hostInfo) +{ + emit bonjourRecordResolved(hostInfo, bonjourPort); + QMetaObject::invokeMethod(this, "cleanupResolve", Qt::QueuedConnection); +} diff --git a/libsrc/hyperion/CMakeLists.txt b/libsrc/hyperion/CMakeLists.txt index 771afb40..71d39022 100644 --- a/libsrc/hyperion/CMakeLists.txt +++ b/libsrc/hyperion/CMakeLists.txt @@ -57,5 +57,6 @@ target_link_libraries(hyperion blackborder hyperion-utils leddevice + bonjour ${QT_LIBRARIES} ) diff --git a/libsrc/hyperion/Hyperion.cpp b/libsrc/hyperion/Hyperion.cpp index 8835e396..2c136c28 100644 --- a/libsrc/hyperion/Hyperion.cpp +++ b/libsrc/hyperion/Hyperion.cpp @@ -3,6 +3,7 @@ #include #include #include +#include // QT includes #include @@ -13,6 +14,7 @@ #include #include #include +#include // hyperion include #include @@ -317,8 +319,7 @@ QSize Hyperion::getLedLayoutGridSize(const QJsonValue& ledsConfig) -LinearColorSmoothing * Hyperion::createColorSmoothing(const QJsonObject & smoothingConfig, LedDevice* leddevice) -{ +LinearColorSmoothing * Hyperion::createColorSmoothing(const QJsonObject & smoothingConfig, LedDevice* leddevice){ QString type = smoothingConfig["type"].toString("linear").toLower(); LinearColorSmoothing * device = nullptr; type = "linear"; // TODO currently hardcoded type, delete it if we have more types @@ -390,12 +391,15 @@ Hyperion::Hyperion(const QJsonObject &qjsonConfig, const QString configFile) , _qjsonConfig(qjsonConfig) , _configFile(configFile) , _timer() + , _timerBonjourResolver() , _log(CORE_LOGGER) , _hwLedCount(_ledString.leds().size()) , _sourceAutoSelectEnabled(true) , _configHash() , _ledGridSize(getLedLayoutGridSize(qjsonConfig["leds"])) , _prevCompId(hyperion::COMP_INVALID) + , _bonjourBrowser(this) + , _bonjourResolver(this) { registerPriority("Off", PriorityMuxer::LOWEST_PRIORITY); @@ -405,6 +409,10 @@ Hyperion::Hyperion(const QJsonObject &qjsonConfig, const QString configFile) } // set color correction activity state const QJsonObject& color = qjsonConfig["color"].toObject(); + + _bonjourBrowser.browseForServiceType(QLatin1String("_hyperiond-http._tcp")); + connect(&_bonjourBrowser, SIGNAL(currentBonjourRecordsChanged(const QList&)),this, SLOT(currentBonjourRecordsChanged(const QList &))); + connect(&_bonjourResolver, SIGNAL(bonjourRecordResolved(const QHostInfo &, int)), this, SLOT(bonjourRecordResolved(const QHostInfo &, int))); // initialize the image processor factory _ledMAppingType = ImageProcessor::mappingTypeToInt(color["imageToLedMappingType"].toString()); @@ -416,11 +424,17 @@ Hyperion::Hyperion(const QJsonObject &qjsonConfig, const QString configFile) _device = LedDeviceFactory::construct(qjsonConfig["device"].toObject(),_hwLedCount); _deviceSmooth = createColorSmoothing(qjsonConfig["smoothing"].toObject(), _device); getComponentRegister().componentStateChanged(hyperion::COMP_SMOOTHING, _deviceSmooth->componentState()); + getComponentRegister().componentStateChanged(hyperion::COMP_LEDDEVICE, _device->componentState()); // setup the timer _timer.setSingleShot(true); QObject::connect(&_timer, SIGNAL(timeout()), this, SLOT(update())); + _timerBonjourResolver.setSingleShot(false); + _timerBonjourResolver.setInterval(1000); + QObject::connect(&_timerBonjourResolver, SIGNAL(timeout()), this, SLOT(bonjourResolve())); + _timerBonjourResolver.start(); + // create the effect engine _effectEngine = new EffectEngine(this,qjsonConfig["effects"].toObject()); @@ -470,6 +484,48 @@ unsigned Hyperion::getLedCount() const return _ledString.leds().size(); } +void Hyperion::currentBonjourRecordsChanged(const QList &list) +{ + _hyperionSessions.clear(); + for ( auto rec : list ) + { + _hyperionSessions.insert(rec.serviceName, rec); + } +} + +void Hyperion::bonjourRecordResolved(const QHostInfo &hostInfo, int port) +{ + if ( _hyperionSessions.contains(_bonjourCurrentServiceToResolve)) + { + QString host = hostInfo.hostName(); + QString domain = _hyperionSessions[_bonjourCurrentServiceToResolve].replyDomain; + if (host.endsWith("."+domain)) + { + host.remove(host.length()-domain.length()-1,domain.length()+1); + } + _hyperionSessions[_bonjourCurrentServiceToResolve].hostName = host; + _hyperionSessions[_bonjourCurrentServiceToResolve].port = port; + Debug(_log, "found hyperion session: %s:%d",QSTRING_CSTR(hostInfo.hostName()), port); + } +} + +void Hyperion::bonjourResolve() +{ + for(auto key : _hyperionSessions.keys()) + { + if (_hyperionSessions[key].port < 0) + { + _bonjourCurrentServiceToResolve = key; + _bonjourResolver.resolveBonjourRecord(_hyperionSessions[key]); + break; + } + } +} + +Hyperion::BonjourRegister Hyperion::getHyperionSessions() +{ + return _hyperionSessions; +} bool Hyperion::configModified() { @@ -507,19 +563,19 @@ void Hyperion::registerPriority(const QString &name, const int priority/*, const { Info(_log, "Register new input source named '%s' for priority channel '%d'", QSTRING_CSTR(name), priority ); - for(auto const &entry : _priorityRegister) + for(auto key : _priorityRegister.keys()) { - WarningIf( ( entry.first != name && entry.second == priority), _log, - "Input source '%s' uses same priority channel (%d) as '%s'.", QSTRING_CSTR(name), priority, QSTRING_CSTR(entry.first)); + WarningIf( ( key != name && _priorityRegister.value(key) == priority), _log, + "Input source '%s' uses same priority channel (%d) as '%s'.", QSTRING_CSTR(name), priority, QSTRING_CSTR(key)); } - _priorityRegister.emplace(name,priority); + _priorityRegister.insert(name, priority); } void Hyperion::unRegisterPriority(const QString &name) { Info(_log, "Unregister input source named '%s' from priority register", QSTRING_CSTR(name)); - _priorityRegister.erase(name); + _priorityRegister.remove(name); } void Hyperion::setSourceAutoSelectEnabled(bool enabled) @@ -550,14 +606,20 @@ bool Hyperion::setCurrentSourcePriority(int priority ) void Hyperion::setComponentState(const hyperion::Components component, const bool state) { - if (component == hyperion::COMP_SMOOTHING) + switch (component) { - _deviceSmooth->setEnable(state); - getComponentRegister().componentStateChanged(hyperion::COMP_SMOOTHING, _deviceSmooth->componentState()); - } - else - { - emit componentStateChanged(component, state); + case hyperion::COMP_SMOOTHING: + _deviceSmooth->setEnable(state); + getComponentRegister().componentStateChanged(hyperion::COMP_SMOOTHING, _deviceSmooth->componentState()); + break; + + case hyperion::COMP_LEDDEVICE: + _device->setEnable(state); + getComponentRegister().componentStateChanged(hyperion::COMP_LEDDEVICE, _device->componentState()); + break; + + default: + emit componentStateChanged(component, state); } } @@ -785,10 +847,13 @@ void Hyperion::update() } // Write the data to the device - if (_deviceSmooth->enabled()) - _deviceSmooth->setLedValues(_ledBuffer); - else - _device->setLedValues(_ledBuffer); + if (_device->enabled()) + { + if (_deviceSmooth->enabled()) + _deviceSmooth->setLedValues(_ledBuffer); + else + _device->setLedValues(_ledBuffer); + } // Start the timeout-timer if (priorityInfo.timeoutTime_ms == -1) @@ -800,4 +865,5 @@ void Hyperion::update() int timeout_ms = std::max(0, int(priorityInfo.timeoutTime_ms - QDateTime::currentMSecsSinceEpoch())); _timer.start(timeout_ms); } + } diff --git a/libsrc/hyperion/LinearColorSmoothing.cpp b/libsrc/hyperion/LinearColorSmoothing.cpp index 2aae81f6..8fae26a6 100644 --- a/libsrc/hyperion/LinearColorSmoothing.cpp +++ b/libsrc/hyperion/LinearColorSmoothing.cpp @@ -17,7 +17,6 @@ LinearColorSmoothing::LinearColorSmoothing( LedDevice * ledDevice, double ledUpd , _outputDelay(updateDelay) , _writeToLedsEnable(true) , _continuousOutput(continuousOutput) - , _enabled(true) { _log = Logger::getInstance("Smoothing"); _timer.setSingleShot(false); @@ -143,7 +142,8 @@ void LinearColorSmoothing::queueColors(const std::vector & ledColors) void LinearColorSmoothing::setEnable(bool enable) { - _enabled = enable; + LedDevice::setEnable(enable); + if (!enable) { _timer.stop(); @@ -151,8 +151,3 @@ void LinearColorSmoothing::setEnable(bool enable) } } - -bool LinearColorSmoothing::enabled() -{ - return _enabled; -} diff --git a/libsrc/hyperion/LinearColorSmoothing.h b/libsrc/hyperion/LinearColorSmoothing.h index d321cb01..d50e2fc7 100644 --- a/libsrc/hyperion/LinearColorSmoothing.h +++ b/libsrc/hyperion/LinearColorSmoothing.h @@ -41,9 +41,6 @@ public: virtual int switchOff(); void setEnable(bool enable); - bool enabled(); - - bool componentState() { return enabled(); }; private slots: /// Timer callback which writes updated led values to the led device @@ -91,6 +88,4 @@ private: /// Flag for dis/enable continuous output to led device regardless there is new data or not bool _continuousOutput; - - bool _enabled; }; diff --git a/libsrc/hyperion/PriorityMuxer.cpp b/libsrc/hyperion/PriorityMuxer.cpp index 7aef7ae9..1aaf8b3c 100644 --- a/libsrc/hyperion/PriorityMuxer.cpp +++ b/libsrc/hyperion/PriorityMuxer.cpp @@ -11,10 +11,12 @@ PriorityMuxer::PriorityMuxer(int ledCount) , _activeInputs() , _lowestPriorityInfo() { - _lowestPriorityInfo.priority = LOWEST_PRIORITY; + _lowestPriorityInfo.priority = LOWEST_PRIORITY; _lowestPriorityInfo.timeoutTime_ms = -1; - _lowestPriorityInfo.ledColors = std::vector(ledCount, {0, 0, 0}); - + _lowestPriorityInfo.ledColors = std::vector(ledCount, {0, 0, 0}); + _lowestPriorityInfo.componentId = hyperion::COMP_COLOR; + _lowestPriorityInfo.origin = "System"; + _activeInputs[_currentPriority] = _lowestPriorityInfo; } diff --git a/libsrc/jsonserver/JsonClientConnection.cpp b/libsrc/jsonserver/JsonClientConnection.cpp index a7cba33d..3ce54531 100644 --- a/libsrc/jsonserver/JsonClientConnection.cpp +++ b/libsrc/jsonserver/JsonClientConnection.cpp @@ -48,6 +48,8 @@ using namespace hyperion; +int _connectionCounter = 0; + JsonClientConnection::JsonClientConnection(QTcpSocket *socket) : QObject() , _socket(socket) @@ -577,6 +579,7 @@ void JsonClientConnection::handleSysInfoCommand(const QJsonObject&, const QStrin system["productVersion"] = data.productVersion; system["prettyName" ] = data.prettyName; system["hostName" ] = data.hostName; + system["domainName" ] = data.domainName; info["system"] = system; QJsonObject hyperion; @@ -619,22 +622,21 @@ void JsonClientConnection::handleServerInfoCommand(const QJsonObject&, const QSt } item["owner"] = QString(hyperion::componentToIdString(priorityInfo.componentId)); - item["componentId"] = priorityInfo.componentId; + item["componentId"] = QString(hyperion::componentToIdString(priorityInfo.componentId)); item["origin"] = priorityInfo.origin; item["component"] = QString(hyperion::componentToString(priorityInfo.componentId)); item["active"] = true; item["visible"] = (priority == currentPriority); - foreach(auto const &entry, priorityRegister) + + // remove item from prio register, because we have more valuable information via active priority + QList prios = priorityRegister.keys(priority); + if (! prios.empty()) { - if (entry.second == priority) - { - item["owner"] = entry.first; - priorityRegister.erase(entry.first); - break; - } + item["owner"] = prios[0]; + priorityRegister.remove(prios[0]); } - if(priorityInfo.componentId == 9) + if(priorityInfo.componentId == hyperion::COMP_COLOR) { QJsonObject LEDcolor; @@ -679,14 +681,15 @@ void JsonClientConnection::handleServerInfoCommand(const QJsonObject&, const QSt // priorities[priorities.size()] = item; priorities.append(item); } - - foreach(auto const &entry, priorityRegister) + + // append left over priorities + for(auto key : priorityRegister.keys()) { QJsonObject item; - item["priority"] = entry.second; + item["priority"] = priorityRegister[key]; item["active"] = false; item["visible"] = false; - item["owner"] = entry.first; + item["owner"] = key; priorities.append(item); } @@ -827,6 +830,22 @@ void JsonClientConnection::handleServerInfoCommand(const QJsonObject&, const QSt QJsonObject hyperion; hyperion["config_modified" ] = _hyperion->configModified(); hyperion["config_writeable"] = _hyperion->configWriteable(); + + // sessions + QJsonArray sessions; + for (auto session: _hyperion->getHyperionSessions()) + { + if (session.port<0) continue; + QJsonObject item; + item["name"] = session.serviceName; + item["type"] = session.registeredType; + item["domain"] = session.replyDomain; + item["host"] = session.hostName; + item["port"] = session.port; + sessions.append(item); + } + hyperion["sessions"] = sessions; + info["hyperion"] = hyperion; // send the result diff --git a/libsrc/jsonserver/schema/schema-componentstate.json b/libsrc/jsonserver/schema/schema-componentstate.json index 28a8a362..0e8db05f 100644 --- a/libsrc/jsonserver/schema/schema-componentstate.json +++ b/libsrc/jsonserver/schema/schema-componentstate.json @@ -21,7 +21,7 @@ "component": { "type" : "string", - "enum" : ["SMOOTHING", "BLACKBORDER", "KODICHECKER", "FORWARDER", "UDPLISTENER", "BOBLIGHTSERVER", "GRABBER", "V4L"], + "enum" : ["SMOOTHING", "BLACKBORDER", "KODICHECKER", "FORWARDER", "UDPLISTENER", "BOBLIGHTSERVER", "GRABBER", "V4L", "LEDDEVICE"], "required": true }, "state": diff --git a/libsrc/leddevice/LedDevice.cpp b/libsrc/leddevice/LedDevice.cpp index 54d825ff..db2228e1 100644 --- a/libsrc/leddevice/LedDevice.cpp +++ b/libsrc/leddevice/LedDevice.cpp @@ -5,6 +5,7 @@ #include #include #include +#include "hyperion/Hyperion.h" LedDeviceRegistry LedDevice::_ledDeviceMap = LedDeviceRegistry(); QString LedDevice::_activeDevice = ""; @@ -19,8 +20,11 @@ LedDevice::LedDevice() , _deviceReady(true) , _refresh_timer() , _refresh_timer_interval(0) + , _componentRegistered(false) + , _enabled(true) { LedDevice::getLedDeviceSchemas(); + qRegisterMetaType("hyperion::Components"); // setup timer _refresh_timer.setSingleShot(false); @@ -34,6 +38,16 @@ int LedDevice::open() return 0; } +void LedDevice::setEnable(bool enable) +{ + if ( _enabled && !enable) + { + switchOff(); + } + _enabled = enable; + +} + int LedDevice::addToDeviceMap(QString name, LedDeviceCreateFuncType funcPtr) { _ledDeviceMap.emplace(name,funcPtr); @@ -111,12 +125,11 @@ QJsonObject LedDevice::getLedDeviceSchemas() return result; } - int LedDevice::setLedValues(const std::vector& ledValues) { - if (!_deviceReady) + if (!_deviceReady || !_enabled) return -1; - + _ledValues = ledValues; // restart the timer @@ -142,5 +155,5 @@ void LedDevice::setLedCount(int ledCount) int LedDevice::rewriteLeds() { - return write(_ledValues); + return _enabled ? write(_ledValues) : -1; } diff --git a/libsrc/utils/SysInfo.cpp b/libsrc/utils/SysInfo.cpp index 92762ec6..dcdcb3ec 100644 --- a/libsrc/utils/SysInfo.cpp +++ b/libsrc/utils/SysInfo.cpp @@ -29,6 +29,7 @@ SysInfo::SysInfo() _sysinfo.productVersion = v.productVersion; _sysinfo.prettyName = v.prettyName; _sysinfo.hostName = QHostInfo::localHostName(); + _sysinfo.domainName = QHostInfo::localDomainName(); } SysInfo::~SysInfo() diff --git a/src/hyperion-remote/hyperion-remote.cpp b/src/hyperion-remote/hyperion-remote.cpp index 8966d946..a34c1baf 100644 --- a/src/hyperion-remote/hyperion-remote.cpp +++ b/src/hyperion-remote/hyperion-remote.cpp @@ -3,6 +3,7 @@ #include #include #include +#include // Qt includes #include @@ -40,6 +41,8 @@ void showHelp(Option & option){ int main(int argc, char * argv[]) { + setenv("AVAHI_COMPAT_NOWARN", "1", 1); + std::cout << "hyperion-remote:" << std::endl << "\tVersion : " << HYPERION_VERSION << " (" << HYPERION_BUILD_ID << ")" << std::endl @@ -70,8 +73,8 @@ int main(int argc, char * argv[]) BooleanOption & argSysInfo = parser.add('s', "sysinfo" , "show system info"); BooleanOption & argClear = parser.add('x', "clear" , "Clear data for the priority channel provided by the -p option"); BooleanOption & argClearAll = parser.add(0x0, "clearall" , "Clear data for all active priority channels"); - Option & argEnableComponent = parser.add