populate zeroconf/avahi/bonjour records via json api (#419)

* start of integrating a bonkour service browser

* some experiments

* blub

* bonjour browser via jsonrpc ...

* fix indention

* - make leddevice as component
- extend sysinfo with domain
- add more data for  bonjour browser (e.g. split domain and hostname)

* code cleanup

* add translation

* use component names instead of ids

* fix compile
This commit is contained in:
redPanther 2017-03-21 17:55:46 +01:00 committed by GitHub
parent 9a0e1daf7b
commit 0aa467cceb
25 changed files with 601 additions and 95 deletions

View File

@ -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",

View File

@ -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",

View File

@ -119,26 +119,24 @@ $(document).ready(function() {
}
if(ip)
origin += '<br/><span style="font-size:80%; color:grey;">'+$.i18n('remote_input_ip')+' '+ip+'</span>';
if(compId == 10)
if(compId == "EFFECT")
owner = $.i18n('remote_effects_label_effects')+' '+owner;
if(compId == 9)
owner = $.i18n('remote_color_label_color')+' '+'<div style="width:18px; height:18px; border-radius:20px; margin-bottom:-4px; border:1px grey solid; background-color: rgb('+prios[i].value.RGB+'); display:inline-block" title="RGB: ('+prios[i].value.RGB+')"></div>';
if(compId == 7)
if(compId == "COLOR")
owner = (owner == "Off") ? $.i18n('general_btn_off') : $.i18n('remote_color_label_color')+' '+'<div style="width:18px; height:18px; border-radius:20px; margin-bottom:-4px; border:1px grey solid; background-color: rgb('+prios[i].value.RGB+'); display:inline-block" title="RGB: ('+prios[i].value.RGB+')"></div>';
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 += '<br/><span style="font-size:80%; color:grey;">'+$.i18n('remote_input_duration')+' '+duration.toFixed(0)+$.i18n('edt_append_s')+'</span>';
var btn = '<button id="srcBtn'+i+'" type="button" '+btn_state+' class="btn btn-'+btn_type+' btn_input_selection" onclick="requestSetSource('+priority+');">'+btn_text+'</button>';
if((compId == 10 || compId == 9) && priority != 254)
if((compId == "EFFECT" || compId == "COLOR") && priority < 254)
btn += '<button type="button" class="btn btn-sm btn-danger" style="margin-left:10px;" onclick="requestPriorityClear('+priority+');"><i class="fa fa-close"></button>';
if(btn_type != 'default')

View File

@ -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 &regType, 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 &regType, 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)

View File

@ -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 <QtCore/QObject>
#include <dns_sd.h>
#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<BonjourRecord> currentRecords() const { return bonjourRecords; }
inline QString serviceType() const { return browsingType; }
signals:
void currentBonjourRecordsChanged(const QList<BonjourRecord> &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<BonjourRecord> bonjourRecords;
QString browsingType;
};
#endif // BONJOURSERVICEBROWSER_H

View File

@ -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 <QtCore/QObject>
#include <dns_sd.h>
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

View File

@ -2,11 +2,12 @@
// stl includes
#include <list>
#include <map>
#include <QMap>
// QT includes
#include <QObject>
#include <QString>
#include <QStringList>
#include <QTimer>
#include <QSize>
#include <QJsonObject>
@ -33,6 +34,8 @@
// KodiVideoChecker includes
#include <kodivideochecker/KODIVideoChecker.h>
#include <bonjour/bonjourservicebrowser.h>
#include <bonjour/bonjourserviceresolver.h>
// 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<QString,int> PriorityRegister;
typedef QMap<QString,int> PriorityRegister;
typedef QMap<QString,BonjourRecord> 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<BonjourRecord> &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<ColorRgb> _ledBuffer;
@ -373,5 +384,9 @@ private:
int _configVersionId;
hyperion::Components _prevCompId;
hyperion::Components _prevCompId;
BonjourServiceBrowser _bonjourBrowser;
BonjourServiceResolver _bonjourResolver;
BonjourRegister _hyperionSessions;
QString _bonjourCurrentServiceToResolve;
};

View File

@ -19,6 +19,7 @@
#include <utils/RgbToRgbw.h>
#include <utils/Logger.h>
#include <functional>
#include <utils/Components.h>
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<ColorRgb> _ledValues;
bool _componentRegistered;
bool _enabled;
};

View File

@ -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;
}

View File

@ -18,6 +18,7 @@ public:
QString productVersion;
QString prettyName;
QString hostName;
QString domainName;
};
~SysInfo();

View File

@ -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}

View File

@ -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 <QtCore/QSocketNotifier>
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<BonjourServiceBrowser *>(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);
}
}
}

View File

@ -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 <QtCore/QSocketNotifier>
#include <QtNetwork/QHostInfo>
#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<BonjourServiceResolver *>(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);
}

View File

@ -57,5 +57,6 @@ target_link_libraries(hyperion
blackborder
hyperion-utils
leddevice
bonjour
${QT_LIBRARIES}
)

View File

@ -3,6 +3,7 @@
#include <cassert>
#include <exception>
#include <sstream>
#include <unistd.h>
// QT includes
#include <QDateTime>
@ -13,6 +14,7 @@
#include <QCryptographicHash>
#include <QFile>
#include <QFileInfo>
#include <QHostInfo>
// hyperion include
#include <hyperion/Hyperion.h>
@ -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<BonjourRecord>&)),this, SLOT(currentBonjourRecordsChanged(const QList<BonjourRecord> &)));
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<BonjourRecord> &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);
}
}

View File

@ -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<ColorRgb> & 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;
}

View File

@ -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;
};

View File

@ -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<ColorRgb>(ledCount, {0, 0, 0});
_lowestPriorityInfo.ledColors = std::vector<ColorRgb>(ledCount, {0, 0, 0});
_lowestPriorityInfo.componentId = hyperion::COMP_COLOR;
_lowestPriorityInfo.origin = "System";
_activeInputs[_currentPriority] = _lowestPriorityInfo;
}

View File

@ -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<QString> 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

View File

@ -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":

View File

@ -5,6 +5,7 @@
#include <QResource>
#include <QStringList>
#include <QDir>
#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>("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<ColorRgb>& 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;
}

View File

@ -29,6 +29,7 @@ SysInfo::SysInfo()
_sysinfo.productVersion = v.productVersion;
_sysinfo.prettyName = v.prettyName;
_sysinfo.hostName = QHostInfo::localHostName();
_sysinfo.domainName = QHostInfo::localDomainName();
}
SysInfo::~SysInfo()

View File

@ -3,6 +3,7 @@
#include <initializer_list>
#include <limits>
#include <iostream>
#include <stdlib.h>
// Qt includes
#include <QCoreApplication>
@ -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<BooleanOption>('s', "sysinfo" , "show system info");
BooleanOption & argClear = parser.add<BooleanOption>('x', "clear" , "Clear data for the priority channel provided by the -p option");
BooleanOption & argClearAll = parser.add<BooleanOption>(0x0, "clearall" , "Clear data for all active priority channels");
Option & argEnableComponent = parser.add<Option> ('E', "enable" , "Enable the Component with the given name. Available Components are [SMOOTHING, BLACKBORDER, KODICHECKER, FORWARDER, UDPLISTENER, BOBLIGHT_SERVER, GRABBER, V4L]");
Option & argDisableComponent = parser.add<Option> ('D', "disable" , "Disable the Component with the given name. Available Components are [SMOOTHING, BLACKBORDER, KODICHECKER, FORWARDER, UDPLISTENER, BOBLIGHT_SERVER, GRABBER, V4L]");
Option & argEnableComponent = parser.add<Option> ('E', "enable" , "Enable the Component with the given name. Available Components are [SMOOTHING, BLACKBORDER, KODICHECKER, FORWARDER, UDPLISTENER, BOBLIGHT_SERVER, GRABBER, V4L, LEDDEVICE]");
Option & argDisableComponent = parser.add<Option> ('D', "disable" , "Disable the Component with the given name. Available Components are [SMOOTHING, BLACKBORDER, KODICHECKER, FORWARDER, UDPLISTENER, BOBLIGHT_SERVER, GRABBER, V4L, LEDDEVICE]");
Option & argId = parser.add<Option> ('q', "qualifier" , "Identifier(qualifier) of the adjustment to set");
DoubleOption & argBrightness = parser.add<DoubleOption> ('L', "brightness" , "Set the brightness gain of the leds");
DoubleOption & argBacklightThreshold= parser.add<DoubleOption> ('n', "backlightThreshold" , "threshold for activating backlight (minimum brightness)");

View File

@ -1,6 +1,7 @@
#include <cassert>
#include <csignal>
#include <unistd.h>
#include <stdlib.h>
#ifndef __APPLE__
/* prctl is Linux only */
@ -60,6 +61,8 @@ void startNewHyperion(int parentPid, std::string hyperionFile, std::string confi
int main(int argc, char** argv)
{
setenv("AVAHI_COMPAT_NOWARN", "1", 1);
// initialize main logger and set global log level
Logger* log = Logger::getInstance("MAIN");
Logger::setLogLevel(Logger::WARNING);