denon avr fixed discovery and added async actions

This commit is contained in:
bernhard.trinnes 2020-06-08 21:27:49 +02:00
parent 99b00d5ac8
commit c03d67b9ff
5 changed files with 770 additions and 188 deletions

View File

@ -1,4 +1,4 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
@ -43,6 +43,23 @@ AvrConnection::AvrConnection(const QHostAddress &hostAddress, const int &port, Q
connect(m_socket, &QTcpSocket::readyRead, this, &AvrConnection::readData);
// Note: error signal will be interpreted as function, not as signal in C++11
connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onError(QAbstractSocket::SocketError)));
m_commandTimer = new QTimer(this);
m_commandTimer->start(50); // 50ms is the minimum request interval specified
connect(m_commandTimer, &QTimer::timeout, this, [this] {
if (!m_commandBuffer.isEmpty()) {
QPair<QUuid, QByteArray> command = m_commandBuffer.takeFirst();
if (m_socket->write(command.second) == -1) {
emit commandExecuted(command.first, false);
qCWarning(dcDenon()) << "Could not execute command" << command.second;
} else {
emit commandExecuted(command.first, true);
}
} else {
m_commandTimer->stop();
}
});
}
AvrConnection::~AvrConnection()
@ -78,56 +95,62 @@ bool AvrConnection::connected()
return m_socket->isOpen();
}
void AvrConnection::getAllStatus()
QUuid AvrConnection::getChannel()
{
sendCommand("PW?\rSI?\rMV?\rMS?\rMU?\r");
return sendCommand("SI?\r");
}
void AvrConnection::getChannel()
QUuid AvrConnection::getVolume()
{
sendCommand("SI?\r");
return sendCommand("MV?\r");
}
void AvrConnection::getVolume()
QUuid AvrConnection::getMute()
{
sendCommand("MV?\r");
return sendCommand("MU?\r");
}
void AvrConnection::getMute()
QUuid AvrConnection::getPower()
{
sendCommand("MU?\r");
return sendCommand("PW?\r");
}
void AvrConnection::getPower()
QUuid AvrConnection::getSurroundMode()
{
sendCommand("PW?\r");
return sendCommand("MS?\r");
}
void AvrConnection::getSurroundMode()
QUuid AvrConnection::getPlayBackInfo()
{
sendCommand("MS?\r");
return sendCommand("NSE\r");
}
void AvrConnection::sendCommand(const QByteArray &message)
QUuid AvrConnection::sendCommand(const QByteArray &message)
{
m_socket->write(message);
QUuid commandId = QUuid::createUuid();
if (!m_commandTimer->isActive())
m_commandTimer->start(50);
m_commandBuffer.append(QPair<QUuid, QByteArray>(commandId, message));
return commandId;
}
void AvrConnection::setChannel(const QByteArray &channel)
QUuid AvrConnection::setChannel(const QByteArray &channel)
{
QByteArray cmd = "SI" + channel + "\r";
qCDebug(dcDenon) << "Change to channel:" << cmd;
sendCommand(cmd);
qCDebug(dcDenon) << "Change to channel:" << channel;
return sendCommand(cmd);
}
void AvrConnection::setVolume(int volume)
QUuid AvrConnection::setVolume(int volume)
{
qCDebug(dcDenon) << "Set volume" << volume;
QByteArray cmd = "MV" + QByteArray::number(volume) + "\r";
sendCommand(cmd);
return sendCommand(cmd);
}
void AvrConnection::setMute(bool mute)
QUuid AvrConnection::setMute(bool mute)
{
qCDebug(dcDenon) << "Set mute" << mute;
QByteArray cmd;
@ -136,10 +159,10 @@ void AvrConnection::setMute(bool mute)
} else {
cmd = "MUOFF\r";
}
sendCommand(cmd);
return sendCommand(cmd);
}
void AvrConnection::setPower(bool power)
QUuid AvrConnection::setPower(bool power)
{
qCDebug(dcDenon) << "Set power" << power;
QByteArray cmd;
@ -148,28 +171,125 @@ void AvrConnection::setPower(bool power)
} else {
cmd = "PWSTANDBY\r";
}
sendCommand(cmd);
return sendCommand(cmd);
}
void AvrConnection::setSurroundMode(const QByteArray &surroundMode)
QUuid AvrConnection::setSurroundMode(const QByteArray &surroundMode)
{
qCDebug(dcDenon) << "Set surround mode" << surroundMode;
QByteArray cmd = "MS" + surroundMode + "\r";
sendCommand(cmd);
return sendCommand(cmd);
}
void AvrConnection::increaseVolume()
QUuid AvrConnection::enableToneControl(bool enabled)
{
QByteArray cmd;
if (enabled) {
cmd = "PSTONE CTRL ON\r";
} else {
cmd = "PSTONE CTRL OFF\r";
}
return sendCommand(cmd);
}
QUuid AvrConnection::setBassLevel(int level)
{
QByteArray cmd;
cmd = "PSBAS ";
cmd.append(50 + level);
cmd.append("\r");
return sendCommand(cmd);
}
QUuid AvrConnection::setTrebleLevel(int level)
{
QByteArray cmd;
cmd = "PSTRE ";
cmd.append(50 + level);
cmd.append("\r");
return sendCommand(cmd);
}
QUuid AvrConnection::getBassLevel()
{
return sendCommand("PSBAS ?\r");
}
QUuid AvrConnection::getTrebleLevel()
{
return sendCommand("PSTRE ?\r");
}
QUuid AvrConnection::getToneControl()
{
return sendCommand("PSTONE CTRL ?\r");
}
QUuid AvrConnection::play()
{
return sendCommand("NS9A\r");
}
QUuid AvrConnection::pause()
{
return sendCommand("NS9B\r");
}
QUuid AvrConnection::stop()
{
return sendCommand("NS9C\r");
}
QUuid AvrConnection::skipNext()
{
return sendCommand("NS9D\r");
}
QUuid AvrConnection::skipBack()
{
return sendCommand("NS9E\r");
}
QUuid AvrConnection::setRandom(bool on)
{
QByteArray cmd;
if (on) {
cmd = "NS9K\r";
} else {
cmd = "NS9M\r";
}
return sendCommand(cmd);
}
QUuid AvrConnection::setRepeat(AvrConnection::RepeatMode mode)
{
QByteArray cmd;
switch (mode) {
case RepeatModeRepeatAll:
cmd = "NS9I\r";
break;
case RepeatModeRepeatOne:
cmd = "NS9H\r";
break;
case RepeatModeRepeatNone:
cmd = "NS9J\r";
break;
}
return sendCommand(cmd);
}
QUuid AvrConnection::increaseVolume()
{
qCDebug(dcDenon) << "Execute volume increase";
QByteArray cmd = "MVUP\r";
sendCommand(cmd);
return sendCommand(cmd);
}
void AvrConnection::decreaseVolume()
QUuid AvrConnection::decreaseVolume()
{
qCDebug(dcDenon) << "Execute volume decrease";
QByteArray cmd = "MVDOWN\r";
sendCommand(cmd);
return sendCommand(cmd);
}
void AvrConnection::onConnected()
@ -192,77 +312,112 @@ void AvrConnection::onError(QAbstractSocket::SocketError socketError)
void AvrConnection::readData()
{
QByteArray data = m_socket->readAll();
qCDebug(dcDenon) << "Data received" << data;
QString data = QString(m_socket->readAll());
if (data.contains("MV") && !data.contains("MAX")){
int index = data.indexOf("MV");
int volume = data.mid(index+2, 2).toInt();
emit volumeChanged(volume);
}
QStringList lines = data.split('\r');
foreach (QString line, lines) {
if(line.isEmpty())
continue;
if (data.left(2).contains("SI")) {
QByteArray cmd;
if (data.contains("TUNER")) {
cmd = "TUNER";
} else if (data.contains("DVD")) {
cmd = "DVD";
} else if (data.contains("BD")) {
cmd = "BD";
} else if (data.contains("TV")) {
cmd = "TV";
} else if (data.contains("SAT/CBL")) {
cmd = "SAT/CBL";
} else if (data.contains("MPLAY")) {
cmd = "MPLAY";
} else if (data.contains("GAME")) {
cmd = "GAME";
} else if (data.contains("AUX1")) {
cmd = "AUX1";
} else if (data.contains("NET")) {
cmd = "NET";
} else if (data.contains("PANDORA")) {
cmd = "PANDORA";
} else if (data.contains("SIRIUSXM")) {
cmd = "SIRIUSXM";
} else if (data.contains("SPOTIFY")) {
cmd = "SPOTIFY";
} else if (data.contains("FLICKR")) {
cmd = "FLICKR";
} else if (data.contains("FAVORITES")) {
cmd = "FAVORITES";
} else if (data.contains("IRADIO")) {
cmd = "IRADIO";
} else if (data.contains("SERVER")) {
cmd = "SERVER";
} else if (data.contains("USB/IPOD")) {
cmd = "USB/IPOD";
} else if (data.contains("IPD")) {
cmd = "IPD";
} else if (data.contains("IRP")) {
cmd = "IRP";
} else if (data.contains("FVP")) {
cmd = "FVP";
qCDebug(dcDenon) << "Data received" << line;
if (line.contains("MV") && !data.contains("MAX")){
int index = data.indexOf("MV");
int volume = data.mid(index+2, 2).toInt();
emit volumeChanged(volume);
} else if (line.left(2).contains("SI")) {
QByteArray cmd;
if (data.contains("TUNER")) {
cmd = "TUNER";
} else if (data.contains("DVD")) {
cmd = "DVD";
} else if (data.contains("BD")) {
cmd = "BD";
} else if (data.contains("TV")) {
cmd = "TV";
} else if (data.contains("SAT/CBL")) {
cmd = "SAT/CBL";
} else if (data.contains("MPLAY")) {
cmd = "MPLAY";
} else if (data.contains("GAME")) {
cmd = "GAME";
} else if (data.contains("AUX1")) {
cmd = "AUX1";
} else if (data.contains("NET")) {
cmd = "NET";
} else if (data.contains("PANDORA")) {
cmd = "PANDORA";
} else if (data.contains("SIRIUSXM")) {
cmd = "SIRIUSXM";
} else if (data.contains("SPOTIFY")) {
cmd = "SPOTIFY";
} else if (data.contains("FLICKR")) {
cmd = "FLICKR";
} else if (data.contains("FAVORITES")) {
cmd = "FAVORITES";
} else if (data.contains("IRADIO")) {
cmd = "IRADIO";
} else if (data.contains("SERVER")) {
cmd = "SERVER";
} else if (data.contains("USB/IPOD")) {
cmd = "USB/IPOD";
} else if (data.contains("IPD")) {
cmd = "IPD";
} else if (data.contains("IRP")) {
cmd = "IRP";
} else if (data.contains("FVP")) {
cmd = "FVP";
}
emit channelChanged(cmd);
} else if (data.contains("PWON")) {
emit powerChanged(true);
} else if (data.contains("PWSTANDBY")) {
emit powerChanged(false);
} else if (data.contains("MUON")) {
emit muteChanged(true);
} else if (data.contains("MUOFF")) {
emit muteChanged(false);
} else if (data.left(2).contains("MS")) {
QString surroundMode = data.remove(0, 2).trimmed();
qCDebug(dcDenon()) << "Surround mode changed" << surroundMode;
emit surroundModeChanged(surroundMode);
} else if (data.left(4).contains("NSE0")) {
QString nowPlaying = QString(data).remove(0, 4).trimmed();
qCDebug(dcDenon()) << "Playbackstatus" << nowPlaying;
if (nowPlaying.contains("Now Playing")) {
emit playBackModeChanged(PlayBackMode::PlayBackModePlaying);
} else {
emit playBackModeChanged(PlayBackMode::PlayBackModeStopped);
}
} else if (data.left(4).contains("NSE1")) {
QString song = QString(data).remove(0, 4).trimmed();
qCDebug(dcDenon()) << "Song" << song;
emit songChanged(song);
} else if (data.left(4).contains("NSE2")) {
QString artist = QString(data).remove(0, 4).trimmed();
qCDebug(dcDenon()) << "Artist" << artist;
emit artistChanged(artist);
} else if (data.left(4).contains("NSE4")) {
QString album = QString(data).remove(0, 4).trimmed();
qCDebug(dcDenon()) << "Album" << album;
emit albumChanged(album);
} else if (data.contains("PSTONE CTRL ON")) {
qCDebug(dcDenon()) << "Tone control is on";
emit toneControlEnabledChanged(true);
} else if (data.contains("PSTONE CTRL OFF")) {
qCDebug(dcDenon()) << "Tone control is off";
emit toneControlEnabledChanged(false);
} else if (data.contains("PSBAS")) {
int index = data.indexOf("PSBAS");
int bass = data.mid(index+6, 2).toInt() - 50;
qCDebug(dcDenon()) << "Bass level" << bass;
emit bassLevelChanged(bass);
} else if (data.contains("PSTRE")) {
int index = data.indexOf("PSTRE");
int treble = data.mid(index+6, 2).toInt() - 50;
qCDebug(dcDenon()) << "Treble level" << treble;
emit trebleLevelChanged(treble);
}
emit channelChanged(cmd);
}
if (data.contains("PWON")) {
emit powerChanged(true);
}
if (data.contains("PWSTANDBY")) {
emit powerChanged(false);
}
if (data.contains("MUON")) {
emit muteChanged(false);
}
if (data.contains("MUOFF")) {
emit muteChanged(false);
}
if (data.left(2).contains("MS")) {
data.remove(0, 2);
QByteArray cmd = data;
emit surroundModeChanged(cmd);
}
}

View File

@ -34,11 +34,25 @@
#include <QObject>
#include <QTcpSocket>
#include <QHostAddress>
#include <QTimer>
#include <QUuid>
class AvrConnection : public QObject
{
Q_OBJECT
public:
enum RepeatMode {
RepeatModeRepeatAll,
RepeatModeRepeatOne,
RepeatModeRepeatNone
};
enum PlayBackMode {
PlayBackModePlaying,
PlayBackModeStopped,
PlayBackModePaused
};
explicit AvrConnection(const QHostAddress &hostAddress, const int &port = 23, QObject *parent = nullptr);
~AvrConnection();
@ -49,27 +63,44 @@ public:
int port() const;
bool connected();
void getAllStatus();
void getChannel();
void getVolume();
void getMute();
void getPower();
void getSurroundMode();
QUuid getChannel();
QUuid getVolume();
QUuid getMute();
QUuid getPower();
QUuid getSurroundMode();
QUuid getPlayBackInfo();
void setChannel(const QByteArray &channel);
void setVolume(int volume);
void setMute(bool mute);
void setPower(bool power);
void setSurroundMode(const QByteArray &surroundMode);
QUuid setChannel(const QByteArray &channel);
QUuid setVolume(int volume);
QUuid setMute(bool mute);
QUuid setPower(bool power);
QUuid setSurroundMode(const QByteArray &surroundMode);
QUuid enableToneControl(bool enabled);
QUuid setBassLevel(int level); //-6 to +6
QUuid setTrebleLevel(int level); //-6 to +6
void increaseVolume();
void decreaseVolume();
QUuid getBassLevel();
QUuid getTrebleLevel();
QUuid getToneControl();
QUuid play();
QUuid pause();
QUuid stop();
QUuid skipNext();
QUuid skipBack();
QUuid setRandom(bool on);
QUuid setRepeat(RepeatMode mode);
QUuid increaseVolume();
QUuid decreaseVolume();
private:
QTimer *m_commandTimer = nullptr;
QTcpSocket *m_socket = nullptr;
QHostAddress m_hostAddress;
int m_port;
QList<QPair<QUuid, QByteArray>> m_commandBuffer;
void sendCommand(const QByteArray &message);
QUuid sendCommand(const QByteArray &message);
private slots:
void onConnected();
@ -80,11 +111,19 @@ private slots:
signals:
void socketErrorOccured(QAbstractSocket::SocketError socketError);
void connectionStatusChanged(bool status);
void commandExecuted(const QUuid &commandId, bool success);
void volumeChanged(int volume);
void muteChanged(bool mute);
void channelChanged(const QByteArray &channel);
void channelChanged(const QString &channel);
void powerChanged(bool power);
void surroundModeChanged(const QByteArray &surroundMode);
void surroundModeChanged(const QString &surroundMode);
void songChanged(const QString &song);
void artistChanged(const QString &artist);
void albumChanged(const QString &album);
void playBackModeChanged(AvrConnection::PlayBackMode);
void bassLevelChanged(int level);
void trebleLevelChanged(int level);
void toneControlEnabledChanged(bool enabled);
};
#endif // AVRCONNECTION_H

View File

@ -58,14 +58,15 @@ void IntegrationPluginDenon::init()
void IntegrationPluginDenon::discoverThings(ThingDiscoveryInfo *info)
{
if (info->thingClassId() == AVRX1000ThingClassId) {
if (!hardwareManager()->zeroConfController()->available() || !hardwareManager()->zeroConfController()->enabled()) {
//: Error discovering Denon things
info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Thing discovery is not available."));
return;
}
if (!m_serviceBrowser) {
m_serviceBrowser = hardwareManager()->zeroConfController()->createServiceBrowser();;
m_serviceBrowser = hardwareManager()->zeroConfController()->createServiceBrowser();
}
if (!hardwareManager()->zeroConfController()->available()) {
qCDebug(dcDenon()) << "Error discovering Denon things. Available:" << hardwareManager()->zeroConfController()->available();
info->finish(Thing::ThingErrorHardwareNotAvailable, "Thing discovery not possible");
return;
}
QTimer::singleShot(2000, info, [this, info](){
@ -196,16 +197,30 @@ void IntegrationPluginDenon::setupThing(ThingSetupInfo *info)
AvrConnection *denonConnection = new AvrConnection(address, 23, this);
connect(denonConnection, &AvrConnection::connectionStatusChanged, this, &IntegrationPluginDenon::onAvrConnectionChanged);
connect(denonConnection, &AvrConnection::socketErrorOccured, this, &IntegrationPluginDenon::onAvrSocketError);
connect(denonConnection, &AvrConnection::commandExecuted, this, &IntegrationPluginDenon::onAvrCommandExecuted);
connect(denonConnection, &AvrConnection::channelChanged, this, &IntegrationPluginDenon::onAvrChannelChanged);
connect(denonConnection, &AvrConnection::powerChanged, this, &IntegrationPluginDenon::onAvrPowerChanged);
connect(denonConnection, &AvrConnection::volumeChanged, this, &IntegrationPluginDenon::onAvrVolumeChanged);
connect(denonConnection, &AvrConnection::surroundModeChanged, this, &IntegrationPluginDenon::onAvrSurroundModeChanged);
connect(denonConnection, &AvrConnection::muteChanged, this, &IntegrationPluginDenon::onAvrMuteChanged);
connect(denonConnection, &AvrConnection::artistChanged, this, &IntegrationPluginDenon::onAvrArtistChanged);
connect(denonConnection, &AvrConnection::albumChanged, this, &IntegrationPluginDenon::onAvrAlbumChanged);
connect(denonConnection, &AvrConnection::songChanged, this, &IntegrationPluginDenon::onAvrSongChanged);
connect(denonConnection, &AvrConnection::playBackModeChanged, this, &IntegrationPluginDenon::onAvrPlayBackModeChanged);
connect(denonConnection, &AvrConnection::bassLevelChanged, this, &IntegrationPluginDenon::onAvrBassLevelChanged);
connect(denonConnection, &AvrConnection::trebleLevelChanged, this, &IntegrationPluginDenon::onAvrTrebleLevelChanged);
connect(denonConnection, &AvrConnection::toneControlEnabledChanged, this, &IntegrationPluginDenon::onAvrToneControlEnabledChanged);
m_avrConnections.insert(thing->id(), denonConnection);
m_asyncAvrSetups.insert(denonConnection, info);
// In case the setup is cancelled before we finish it...
connect(info, &QObject::destroyed, this, [this, denonConnection]() { m_asyncAvrSetups.remove(denonConnection); });
connect(info, &ThingSetupInfo::aborted, this, [this, thing] () {
if (m_avrConnections.contains(thing->id())) {
AvrConnection *connection = m_avrConnections.take(thing->id());
connection->deleteLater();
}
});
denonConnection->connectDevice();
return;
} else if (thing->thingClassId() == heosThingClassId) {
@ -273,48 +288,120 @@ void IntegrationPluginDenon::executeAction(ThingActionInfo *info)
if (thing->thingClassId() == AVRX1000ThingClassId) {
AvrConnection *avrConnection = m_avrConnections.value(thing->id());
if (action.actionTypeId() == AVRX1000PowerActionTypeId) {
if (action.actionTypeId() == AVRX1000PlayActionTypeId) {
QUuid commandId = avrConnection->play();
connect(info, &ThingActionInfo::aborted, [this, commandId] {m_avrPendingActions.remove(commandId);});
m_avrPendingActions.insert(commandId, info);
} else if (action.actionTypeId() == AVRX1000PauseActionTypeId) {
QUuid commandId = avrConnection->pause();
connect(info, &ThingActionInfo::aborted, [this, commandId] {m_avrPendingActions.remove(commandId);});
m_avrPendingActions.insert(commandId, info);
} else if (action.actionTypeId() == AVRX1000StopActionTypeId) {
QUuid commandId = avrConnection->stop();
connect(info, &ThingActionInfo::aborted, [this, commandId] {m_avrPendingActions.remove(commandId);});
m_avrPendingActions.insert(commandId, info);
} else if (action.actionTypeId() == AVRX1000SkipNextActionTypeId) {
QUuid commandId = avrConnection->skipNext();
connect(info, &ThingActionInfo::aborted, [this, commandId] {m_avrPendingActions.remove(commandId);});
m_avrPendingActions.insert(commandId, info);
} else if (action.actionTypeId() == AVRX1000SkipBackActionTypeId) {
QUuid commandId = avrConnection->skipBack();
connect(info, &ThingActionInfo::aborted, [this, commandId] {m_avrPendingActions.remove(commandId);});
m_avrPendingActions.insert(commandId, info);
} else if (action.actionTypeId() == AVRX1000PowerActionTypeId) {
bool power = action.param(AVRX1000PowerActionPowerParamTypeId).value().toBool();
avrConnection->setPower(power);
return info->finish(Thing::ThingErrorNoError);
QUuid commandId = avrConnection->setPower(power);
connect(info, &ThingActionInfo::aborted, [this, commandId] {m_avrPendingActions.remove(commandId);});
m_avrPendingActions.insert(commandId, info);
} else if (action.actionTypeId() == AVRX1000VolumeActionTypeId) {
int vol = action.param(AVRX1000VolumeActionVolumeParamTypeId).value().toInt();
avrConnection->setVolume(vol);
return info->finish(Thing::ThingErrorNoError);
QUuid commandId = avrConnection->setVolume(vol);
connect(info, &ThingActionInfo::aborted, [this, commandId] {m_avrPendingActions.remove(commandId);});
m_avrPendingActions.insert(commandId, info);
} else if (action.actionTypeId() == AVRX1000ChannelActionTypeId) {
qCDebug(dcDenon) << "Execute update action";
QByteArray channel = action.param(AVRX1000ChannelActionChannelParamTypeId).value().toByteArray();
avrConnection->setChannel(channel);
return info->finish(Thing::ThingErrorNoError);
QUuid commandId = avrConnection->setChannel(channel);
connect(info, &ThingActionInfo::aborted, [this, commandId] {m_avrPendingActions.remove(commandId);});
m_avrPendingActions.insert(commandId, info);
} else if (action.actionTypeId() == AVRX1000IncreaseVolumeActionTypeId) {
avrConnection->increaseVolume();
return info->finish(Thing::ThingErrorNoError);
QUuid commandId = avrConnection->increaseVolume();
connect(info, &ThingActionInfo::aborted, [this, commandId] {m_avrPendingActions.remove(commandId);});
m_avrPendingActions.insert(commandId, info);
} else if (action.actionTypeId() == AVRX1000DecreaseVolumeActionTypeId) {
avrConnection->decreaseVolume();
return info->finish(Thing::ThingErrorNoError);
QUuid commandId = avrConnection->decreaseVolume();
connect(info, &ThingActionInfo::aborted, [this, commandId] {m_avrPendingActions.remove(commandId);});
m_avrPendingActions.insert(commandId, info);
} else if (action.actionTypeId() == AVRX1000SurroundModeActionTypeId) {
QByteArray surroundMode = action.param(AVRX1000SurroundModeActionSurroundModeParamTypeId).value().toByteArray();
avrConnection->setSurroundMode(surroundMode);
return info->finish(Thing::ThingErrorNoError);
QUuid commandId = avrConnection->setSurroundMode(surroundMode);
connect(info, &ThingActionInfo::aborted, [this, commandId] {m_avrPendingActions.remove(commandId);});
m_avrPendingActions.insert(commandId, info);
} else if (action.actionTypeId() == AVRX1000MuteActionTypeId) {
bool mute = action.param(AVRX1000MuteActionMuteParamTypeId).value().toBool();
QUuid commandId = avrConnection->setMute(mute);
connect(info, &ThingActionInfo::aborted, [this, commandId] {m_avrPendingActions.remove(commandId);});
m_avrPendingActions.insert(commandId, info);
} else if (action.actionTypeId() == AVRX1000RepeatActionTypeId) {
QString repeatMode = action.param(AVRX1000RepeatActionRepeatParamTypeId).value().toString();
QUuid commandId;
if (repeatMode == "One") {
commandId = avrConnection->setRepeat(AvrConnection::RepeatModeRepeatOne);
} else if (repeatMode == "All") {
commandId = avrConnection->setRepeat(AvrConnection::RepeatModeRepeatAll);
} else {
commandId = avrConnection->setRepeat(AvrConnection::RepeatModeRepeatNone);
}
connect(info, &ThingActionInfo::aborted, [this, commandId] {m_avrPendingActions.remove(commandId);});
m_avrPendingActions.insert(commandId, info);
} else if (action.actionTypeId() == AVRX1000ShuffleActionTypeId) {
bool shuffle = action.param(AVRX1000ShuffleActionShuffleParamTypeId).value().toBool();
QUuid commandId = avrConnection->setRandom(shuffle);
connect(info, &ThingActionInfo::aborted, [this, commandId] {m_avrPendingActions.remove(commandId);});
m_avrPendingActions.insert(commandId, info);
} else if (action.actionTypeId() == AVRX1000PlaybackStatusActionTypeId) {
QString playbackStatus = action.param(AVRX1000PlaybackStatusActionPlaybackStatusParamTypeId).value().toString();
QUuid commandId;
if (playbackStatus == "Playing") {
commandId = avrConnection->play();
} else if (playbackStatus == "Stopped") {
commandId = avrConnection->stop();
} else if (playbackStatus == "Paused") {
commandId = avrConnection->pause();
} else {
qCWarning(dcDenon()) << "Unrecognized playback status" << playbackStatus;
return info->finish(Thing::ThingErrorHardwareFailure, "Unrecognized command");
}
connect(info, &ThingActionInfo::aborted, [this, commandId] {m_avrPendingActions.remove(commandId);});
m_avrPendingActions.insert(commandId, info);
} else if (action.actionTypeId() == AVRX1000ToneControlActionTypeId) {
bool enable = action.param(AVRX1000ToneControlActionToneControlParamTypeId).value().toBool();
QUuid commandId = avrConnection->enableToneControl(enable);
connect(info, &ThingActionInfo::aborted, [this, commandId] {m_avrPendingActions.remove(commandId);});
m_avrPendingActions.insert(commandId, info);
} else if (action.actionTypeId() == AVRX1000BassActionTypeId) {
int bass = action.param(AVRX1000BassActionBassParamTypeId).value().toInt();
QUuid commandId = avrConnection->setBassLevel(bass);
connect(info, &ThingActionInfo::aborted, [this, commandId] {m_avrPendingActions.remove(commandId);});
m_avrPendingActions.insert(commandId, info);
} else if (action.actionTypeId() == AVRX1000TrebleActionTypeId) {
int treble = action.param(AVRX1000TrebleActionTrebleParamTypeId).value().toInt();
QUuid commandId = avrConnection->setTrebleLevel(treble);
connect(info, &ThingActionInfo::aborted, [this, commandId] {m_avrPendingActions.remove(commandId);});
m_avrPendingActions.insert(commandId, info);
} else {
qCWarning(dcDenon()) << "ActionType not found" << thing->thingClass().name() << action.actionTypeId() ;
return info->finish(Thing::ThingErrorActionTypeNotFound);
}
return info->finish(Thing::ThingErrorActionTypeNotFound);
} else if (thing->thingClassId() == heosThingClassId) {
Heos *heos = m_heosConnections.value(thing->id());
if (action.actionTypeId() == heosRebootActionTypeId) {
heos->rebootSpeaker();
return info->finish(Thing::ThingErrorNoError);
} else {
qCWarning(dcDenon()) << "ActionType not found" << thing->thingClass().name() << action.actionTypeId() ;
return info->finish(Thing::ThingErrorActionTypeNotFound);
}
} else if (thing->thingClassId() == heosPlayerThingClassId) {
@ -369,16 +456,31 @@ void IntegrationPluginDenon::executeAction(ThingActionInfo *info)
heos->playNext(playerId);
return info->finish(Thing::ThingErrorNoError);
} else {
qCWarning(dcDenon()) << "ActionType not found" << thing->thingClass().name() << action.actionTypeId() ;
return info->finish(Thing::ThingErrorActionTypeNotFound);
}
} else {
qCWarning(dcDenon()) << "ThingClass not found" << thing->thingClass().name() << thing->thingClassId() ;
return info->finish(Thing::ThingErrorThingClassNotFound);
}
}
void IntegrationPluginDenon::postSetupThing(Thing *thing)
{
if (thing->thingClassId() == heosThingClassId) {
if (thing->thingClassId() == AVRX1000ThingClassId) {
AvrConnection *avrConnection = m_avrConnections.value(thing->id());
thing->setStateValue(AVRX1000ConnectedStateTypeId, avrConnection->connected());
avrConnection->getPower();
avrConnection->getMute();
avrConnection->getVolume();
avrConnection->getChannel();
avrConnection->getSurroundMode();
avrConnection->getPlayBackInfo();
avrConnection->getBassLevel();
avrConnection->getTrebleLevel();
avrConnection->getToneControl();
} else if (thing->thingClassId() == heosThingClassId) {
Heos *heos = m_heosConnections.value(thing->id());
thing->setStateValue(heosConnectedStateTypeId, heos->connected());
if (pluginStorage()->childGroups().contains(thing->id().toString())) {
@ -414,13 +516,21 @@ void IntegrationPluginDenon::postSetupThing(Thing *thing)
void IntegrationPluginDenon::onPluginTimer()
{
foreach(AvrConnection *denonConnection, m_avrConnections.values()) {
if (!denonConnection->connected()) {
denonConnection->connectDevice();
foreach(AvrConnection *avrConnection, m_avrConnections.values()) {
if (!avrConnection->connected()) {
avrConnection->connectDevice();
}
Thing *thing = myThings().findById(m_avrConnections.key(denonConnection));
Thing *thing = myThings().findById(m_avrConnections.key(avrConnection));
if (thing->thingClassId() == AVRX1000ThingClassId) {
denonConnection->getAllStatus();
avrConnection->getPower();
avrConnection->getMute();
avrConnection->getVolume();
avrConnection->getChannel();
avrConnection->getSurroundMode();
avrConnection->getPlayBackInfo();
avrConnection->getBassLevel();
avrConnection->getTrebleLevel();
avrConnection->getToneControl();
}
}
@ -437,19 +547,25 @@ void IntegrationPluginDenon::onPluginTimer()
void IntegrationPluginDenon::onAvrConnectionChanged(bool status)
{
AvrConnection *denonConnection = static_cast<AvrConnection *>(sender());
Thing *thing = myThings().findById(m_avrConnections.key(denonConnection));
if (!thing)
// if the thing and from the first setup
if (m_asyncAvrSetups.contains(denonConnection)) {
// and ist connected
if (status) {
ThingSetupInfo *info = m_asyncAvrSetups.take(denonConnection);
info->thing()->setStateValue(AVRX1000ConnectedStateTypeId, true);
info->finish(Thing::ThingErrorNoError);
}
return;
}
Thing *thing = myThings().findById(m_avrConnections.key(denonConnection));
if (!thing) {
qCWarning(dcDenon()) << "Could not find a thing associated to this AVR connection";
return;
}
if (thing->thingClassId() == AVRX1000ThingClassId) {
// if the thing is connected
if (status) {
// and from the first setup
if (m_asyncAvrSetups.contains(denonConnection)) {
ThingSetupInfo *info = m_asyncAvrSetups.take(denonConnection);
info->finish(Thing::ThingErrorNoError);
}
}
thing->setStateValue(AVRX1000ConnectedStateTypeId, denonConnection->connected());
}
}
@ -458,15 +574,17 @@ void IntegrationPluginDenon::onAvrVolumeChanged(int volume)
{
AvrConnection *denonConnection = static_cast<AvrConnection *>(sender());
Thing *thing = myThings().findById(m_avrConnections.key(denonConnection));
if (!thing)
if (!thing) {
qCWarning(dcDenon()) << "Could not find a thing associated to this AVR connection";
return;
}
if (thing->thingClassId() == AVRX1000ThingClassId) {
thing->setStateValue(AVRX1000VolumeStateTypeId, volume);
}
}
void IntegrationPluginDenon::onAvrChannelChanged(const QByteArray &channel)
void IntegrationPluginDenon::onAvrChannelChanged(const QString &channel)
{
AvrConnection *denonConnection = static_cast<AvrConnection *>(sender());
Thing *thing = myThings().findById(m_avrConnections.key(denonConnection));
@ -482,8 +600,10 @@ void IntegrationPluginDenon::onAvrMuteChanged(bool mute)
{
AvrConnection *denonConnection = static_cast<AvrConnection *>(sender());
Thing *thing = myThings().findById(m_avrConnections.key(denonConnection));
if (!thing)
if (!thing) {
qCWarning(dcDenon()) << "Could not find a thing associated to this AVR connection";
return;
}
if (thing->thingClassId() == AVRX1000ThingClassId) {
thing->setStateValue(AVRX1000MuteStateTypeId, mute);
@ -502,37 +622,157 @@ void IntegrationPluginDenon::onAvrPowerChanged(bool power)
}
}
void IntegrationPluginDenon::onAvrSurroundModeChanged(const QByteArray &surroundMode)
void IntegrationPluginDenon::onAvrSurroundModeChanged(const QString &surroundMode)
{
AvrConnection *denonConnection = static_cast<AvrConnection *>(sender());
Thing *thing = myThings().findById(m_avrConnections.key(denonConnection));
if (!thing)
if (!thing){
qCWarning(dcDenon()) << "Could not find a thing associated to this AVR connection";
return;
}
if (thing->thingClassId() == AVRX1000ThingClassId) {
thing->setStateValue(AVRX1000SurroundModeStateTypeId, surroundMode);
}
}
void IntegrationPluginDenon::onAvrSongChanged(const QString &song)
{
AvrConnection *denonConnection = static_cast<AvrConnection *>(sender());
Thing *thing = myThings().findById(m_avrConnections.key(denonConnection));
if (!thing){
qCWarning(dcDenon()) << "Could not find a thing associated to this AVR connection";
return;
}
if (thing->thingClassId() == AVRX1000ThingClassId) {
thing->setStateValue(AVRX1000TitleStateTypeId, song);
}
}
void IntegrationPluginDenon::onAvrArtistChanged(const QString &artist)
{
AvrConnection *denonConnection = static_cast<AvrConnection *>(sender());
Thing *thing = myThings().findById(m_avrConnections.key(denonConnection));
if (!thing){
qCWarning(dcDenon()) << "Could not find a thing associated to this AVR connection";
return;
}
if (thing->thingClassId() == AVRX1000ThingClassId) {
thing->setStateValue(AVRX1000ArtistStateTypeId, artist);
}
}
void IntegrationPluginDenon::onAvrAlbumChanged(const QString &album)
{
AvrConnection *denonConnection = static_cast<AvrConnection *>(sender());
Thing *thing = myThings().findById(m_avrConnections.key(denonConnection));
if (!thing){
qCWarning(dcDenon()) << "Could not find a thing associated to this AVR connection";
return;
}
if (thing->thingClassId() == AVRX1000ThingClassId) {
thing->setStateValue(AVRX1000CollectionStateTypeId, album);
}
}
void IntegrationPluginDenon::onAvrBassLevelChanged(int level)
{
AvrConnection *denonConnection = static_cast<AvrConnection *>(sender());
Thing *thing = myThings().findById(m_avrConnections.key(denonConnection));
if (!thing){
qCWarning(dcDenon()) << "Could not find a thing associated to this AVR connection";
return;
}
if (thing->thingClassId() == AVRX1000ThingClassId) {
thing->setStateValue(AVRX1000BassStateTypeId, level);
}
}
void IntegrationPluginDenon::onAvrTrebleLevelChanged(int level)
{
AvrConnection *denonConnection = static_cast<AvrConnection *>(sender());
Thing *thing = myThings().findById(m_avrConnections.key(denonConnection));
if (!thing){
qCWarning(dcDenon()) << "Could not find a thing associated to this AVR connection";
return;
}
if (thing->thingClassId() == AVRX1000ThingClassId) {
thing->setStateValue(AVRX1000TrebleStateTypeId, level);
}
}
void IntegrationPluginDenon::onAvrToneControlEnabledChanged(bool enabled)
{
AvrConnection *denonConnection = static_cast<AvrConnection *>(sender());
Thing *thing = myThings().findById(m_avrConnections.key(denonConnection));
if (!thing){
qCWarning(dcDenon()) << "Could not find a thing associated to this AVR connection";
return;
}
if (thing->thingClassId() == AVRX1000ThingClassId) {
thing->setStateValue(AVRX1000ToneControlStateTypeId, enabled);
}
}
void IntegrationPluginDenon::onAvrPlayBackModeChanged(AvrConnection::PlayBackMode mode)
{
AvrConnection *denonConnection = static_cast<AvrConnection *>(sender());
Thing *thing = myThings().findById(m_avrConnections.key(denonConnection));
if (!thing){
qCWarning(dcDenon()) << "Could not find a thing associated to this AVR connection";
return;
}
if (thing->thingClassId() == AVRX1000ThingClassId) {
switch (mode) {
case AvrConnection::PlayBackModePlaying:
thing->setStateValue(AVRX1000PlaybackStatusStateTypeId, "Playing");
break;
case AvrConnection::PlayBackModePaused:
thing->setStateValue(AVRX1000PlaybackStatusStateTypeId, "Paused");
break;
case AvrConnection::PlayBackModeStopped:
thing->setStateValue(AVRX1000PlaybackStatusStateTypeId, "Stopped");
break;
}
}
}
void IntegrationPluginDenon::onAvrSocketError()
{
AvrConnection *denonConnection = static_cast<AvrConnection *>(sender());
Thing *thing = myThings().findById(m_avrConnections.key(denonConnection));
if (!thing)
return;
if (thing->thingClassId() == AVRX1000ThingClassId) {
// Check if setup running for this thing
if (m_asyncAvrSetups.contains(denonConnection)) {
ThingSetupInfo *info = m_asyncAvrSetups.take(denonConnection);
qCWarning(dcDenon()) << "Could not add thing. The setup failed.";
info->finish(Thing::ThingErrorHardwareFailure);
// Delete the connection, the thing will not be added and
// the connection will be created in the next setup
denonConnection->deleteLater();
}
}
// Check if setup running for this thing
if (m_asyncAvrSetups.contains(denonConnection)) {
ThingSetupInfo *info = m_asyncAvrSetups.take(denonConnection);
qCWarning(dcDenon()) << "Could not add thing. The setup failed.";
info->finish(Thing::ThingErrorHardwareFailure);
// Delete the connection, the thing will not be added and
// the connection will be created in the next setup
denonConnection->deleteLater();
m_avrConnections.remove(thing->id());
void IntegrationPluginDenon::onAvrCommandExecuted(const QUuid &commandId, bool success)
{
if (m_avrPendingActions.contains(commandId)) {
ThingActionInfo *info = m_avrPendingActions.take(commandId);
if (success){
if(info->action().actionTypeId() == AVRX1000PlayActionTypeId) {
info->thing()->setStateValue(AVRX1000PlaybackStatusStateTypeId, "Playing");
} else if(info->action().actionTypeId() == AVRX1000PauseActionTypeId) {
info->thing()->setStateValue(AVRX1000PlaybackStatusStateTypeId, "Paused");
} else if(info->action().actionTypeId() == AVRX1000StopActionTypeId) {
info->thing()->setStateValue(AVRX1000PlaybackStatusStateTypeId, "Stopped");
} else if(info->action().actionTypeId() == AVRX1000PlaybackStatusActionTypeId) {
info->thing()->setStateValue(AVRX1000PlaybackStatusStateTypeId, info->action().param(AVRX1000PlaybackStatusActionPlaybackStatusParamTypeId).value());
}
info->finish(Thing::ThingErrorNoError);
} else {
info->finish(Thing::ThingErrorHardwareNotAvailable);
}
}
}
@ -663,7 +903,7 @@ void IntegrationPluginDenon::onHeosRepeatModeReceived(int playerId, REPEAT_MODE
void IntegrationPluginDenon::onHeosShuffleModeReceived(int playerId, bool shuffle)
{
foreach(Thing *thing, myThings().filterByParam(heosPlayerThingPlayerIdParamTypeId, playerId)) {
thing->setStateValue(heosPlayerMuteStateTypeId, shuffle);
thing->setStateValue(heosPlayerShuffleStateTypeId, shuffle);
break;
}
}

View File

@ -90,7 +90,8 @@ private:
QHash<const Action *, int> m_asyncActions;
QUrl m_notificationUrl;
QHash<int, ThingActionInfo*> m_pendingActions;
QHash<int, ThingActionInfo*> m_heosPendingActions;
QHash<QUuid, ThingActionInfo*> m_avrPendingActions;
QHash<Heos*, BrowseResult*> m_pendingGetSourcesRequest;
QHash<QString, BrowseResult*> m_pendingBrowseResult; // QString = containerId or sourceId
@ -128,11 +129,20 @@ private slots:
void onAvrConnectionChanged(bool status);
void onAvrSocketError();
void onAvrCommandExecuted(const QUuid &commandId, bool success);
void onAvrVolumeChanged(int volume);
void onAvrChannelChanged(const QByteArray &channel);
void onAvrChannelChanged(const QString &channel);
void onAvrMuteChanged(bool mute);
void onAvrPowerChanged(bool power);
void onAvrSurroundModeChanged(const QByteArray &surroundMode);
void onAvrSurroundModeChanged(const QString &surroundMode);
void onAvrSongChanged(const QString &song);
void onAvrArtistChanged(const QString &artist);
void onAvrAlbumChanged(const QString &album);
void onAvrPlayBackModeChanged(AvrConnection::PlayBackMode mode);
void onAvrBassLevelChanged(int level);
void onAvrTrebleLevelChanged(int level);
void onAvrToneControlEnabledChanged(bool enabled);
void onPluginConfigurationChanged(const ParamTypeId &paramTypeId, const QVariant &value);
};

View File

@ -22,7 +22,7 @@
"name": "AVRX1000",
"displayName": "AVR X1000",
"createMethods": ["discovery"],
"interfaces": ["extendedvolumecontroller", "connectable", "power"],
"interfaces": ["mediaplayer", "mediacontroller", "extendedvolumecontroller", "mediametadataprovider", "shufflerepeat", "connectable", "power"],
"paramTypes": [
{
"id": "cb6eeeb0-3d75-43b6-8177-b5ac19648557",
@ -49,7 +49,7 @@
"cached": false
},
{
"displayName": "power",
"displayName": "Power",
"id": "1cdb6b54-6831-4900-95b2-c78f64497701",
"name": "power",
"displayNameEvent": "Power changed",
@ -80,6 +80,40 @@
"maxValue": 100,
"writable": true
},
{
"displayName": "Tone control",
"id": "d57c1e5e-2cc9-4638-999c-1523f16dbb83",
"name": "toneControl",
"displayNameEvent": "Tone control changed",
"displayNameAction": "Set tone control",
"type": "bool",
"defaultValue": false,
"writable": true
},
{
"displayName": "Bass",
"id": "2c92b22e-d5b2-4991-a523-64222bffc9e7",
"name": "bass",
"displayNameEvent": "Bass changed",
"displayNameAction": "Set bass",
"type": "int",
"defaultValue": 0,
"minValue": -50,
"maxValue": 49,
"writable": true
},
{
"displayName": "Treble",
"id": "38a3be02-6ed4-4a84-903e-eb923b933989",
"name": "treble",
"displayNameEvent": "Treble changed",
"displayNameAction": "Set treble",
"type": "int",
"defaultValue": 0,
"minValue": -50,
"maxValue": 49,
"writable": true
},
{
"displayName": "Channel",
"id": "f29ffa2c-31d6-4d88-b160-a38288c82ce1",
@ -136,6 +170,85 @@
"MATRIX"
],
"defaultValue": "MOVIE"
},
{
"id": "50b34df4-90f7-41aa-9a57-6f2d31a18430",
"name": "artist",
"displayName": "Artist",
"displayNameEvent": "Artist changed",
"type": "QString",
"defaultValue": ""
},
{
"id": "d4f14d93-8cae-4e1b-9ee7-a5c21c0211df",
"name": "collection",
"displayName": "Album",
"displayNameEvent": "Album changed",
"type": "QString",
"defaultValue": ""
},
{
"id": "d32493d8-5faf-4c7a-ba94-847dc9b81615",
"name": "title",
"displayName": "Title",
"displayNameEvent": "Title changed",
"type": "QString",
"defaultValue": ""
},
{
"id": "6c4a208b-5b04-40cc-b14b-8f79aff30307",
"name": "artwork",
"displayName": "Artwork",
"displayNameEvent": "Artwork changed",
"type": "QString",
"defaultValue": ""
},
{
"id": "2f372374-16f3-4900-afdc-834f51075d07",
"name": "playerType",
"displayName": "Player type",
"displayNameEvent": "Player type changed",
"possibleValues": [
"audio",
"video"
],
"type": "QString",
"defaultValue": "audio"
},
{
"id": "8ef6708c-812a-4e6a-a608-9e480aa3b7bf",
"name": "playbackStatus",
"displayName": "Playback status",
"displayNameEvent": "Playback status changed",
"displayNameAction": "Set playback status",
"type": "QString",
"defaultValue": "Stopped",
"possibleValues": ["Playing", "Paused", "Stopped"],
"cached": false,
"writable": true
},
{
"id": "8ad33cb9-e758-433d-a013-2e4d43157c92",
"name": "shuffle",
"displayName": "Shuffle",
"displayNameEvent": "Shuffle changed",
"displayNameAction": "Set shuffle",
"type": "bool",
"defaultValue": false,
"cached": false,
"writable": true
},
{
"id": "9478987b-14e4-4572-a059-a18a5a9db229",
"name": "repeat",
"displayName": "Repeat mode",
"displayNameEvent": "Repeat mode changed",
"displayNameAction": "Set repeat mode",
"type": "QString",
"defaultValue": "None",
"possibleValues": ["None", "One", "All"],
"cached": false,
"writable": true
}
],
"actionTypes": [
@ -164,6 +277,31 @@
"type": "int"
}
]
},
{
"id": "3f2eb789-918c-475a-a295-14c0c24338b8",
"name": "skipBack",
"displayName": "Skip back"
},
{
"id": "ddab0869-5b90-4fd4-9c40-9ad57101b87c",
"name": "stop",
"displayName": "Stop"
},
{
"id": "d04eb30b-838d-4fbd-b781-c01005b59756",
"name": "play",
"displayName": "Play"
},
{
"id": "3de38047-006f-4d97-9326-08bb5ad79b05",
"name": "pause",
"displayName": "Pause"
},
{
"id": "bf9664e4-a53e-474c-afcc-88f25e6fe365",
"name": "skipNext",
"displayName": "Skip next"
}
]
},