Merge PR #102: Add heos devices to denon plug-in
This commit is contained in:
commit
0d4237d390
261
denon/avrconnection.cpp
Normal file
261
denon/avrconnection.cpp
Normal file
@ -0,0 +1,261 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2015 Simon Stürz <simon.stuerz@nymea.io> *
|
||||
* Copyright (C) 2019 Bernhard Trinnes <bernhard.trinnes@nymea.io> *
|
||||
* *
|
||||
* This file is part of nymea. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Lesser General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2.1 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; If not, see *
|
||||
* <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "avrconnection.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
AvrConnection::AvrConnection(const QHostAddress &hostAddress, const int &port, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_hostAddress(hostAddress),
|
||||
m_port(port)
|
||||
{
|
||||
m_socket = new QTcpSocket(this);
|
||||
|
||||
connect(m_socket, &QTcpSocket::connected, this, &AvrConnection::onConnected);
|
||||
connect(m_socket, &QTcpSocket::disconnected, this, &AvrConnection::onDisconnected);
|
||||
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)));
|
||||
}
|
||||
|
||||
AvrConnection::~AvrConnection()
|
||||
{
|
||||
m_socket->close();
|
||||
}
|
||||
|
||||
void AvrConnection::connectDevice()
|
||||
{
|
||||
if (m_socket->state() == QAbstractSocket::ConnectingState) {
|
||||
return;
|
||||
}
|
||||
m_socket->connectToHost(m_hostAddress, m_port);
|
||||
}
|
||||
|
||||
void AvrConnection::disconnectDevice()
|
||||
{
|
||||
m_socket->close();
|
||||
}
|
||||
|
||||
QHostAddress AvrConnection::hostAddress() const
|
||||
{
|
||||
return m_hostAddress;
|
||||
}
|
||||
|
||||
int AvrConnection::port() const
|
||||
{
|
||||
return m_port;
|
||||
}
|
||||
|
||||
bool AvrConnection::connected()
|
||||
{
|
||||
return m_socket->isOpen();
|
||||
}
|
||||
|
||||
void AvrConnection::getAllStatus()
|
||||
{
|
||||
sendCommand("PW?\rSI?\rMV?\rMS?\rMU?\r");
|
||||
}
|
||||
|
||||
void AvrConnection::getChannel()
|
||||
{
|
||||
sendCommand("SI?\r");
|
||||
}
|
||||
|
||||
void AvrConnection::getVolume()
|
||||
{
|
||||
sendCommand("MV?\r");
|
||||
}
|
||||
|
||||
void AvrConnection::getMute()
|
||||
{
|
||||
sendCommand("MU?\r");
|
||||
}
|
||||
|
||||
void AvrConnection::getPower()
|
||||
{
|
||||
sendCommand("PW?\r");
|
||||
}
|
||||
|
||||
void AvrConnection::getSurroundMode()
|
||||
{
|
||||
sendCommand("MS?\r");
|
||||
}
|
||||
|
||||
void AvrConnection::sendCommand(const QByteArray &message)
|
||||
{
|
||||
m_socket->write(message);
|
||||
}
|
||||
|
||||
void AvrConnection::setChannel(const QByteArray &channel)
|
||||
{
|
||||
QByteArray cmd = "SI" + channel + "\r";
|
||||
qCDebug(dcDenon) << "Change to channel:" << cmd;
|
||||
sendCommand(cmd);
|
||||
}
|
||||
|
||||
void AvrConnection::setVolume(int volume)
|
||||
{
|
||||
qCDebug(dcDenon) << "Set volume" << volume;
|
||||
QByteArray cmd = "MV" + QByteArray::number(volume) + "\r";
|
||||
sendCommand(cmd);
|
||||
}
|
||||
|
||||
void AvrConnection::setMute(bool mute)
|
||||
{
|
||||
qCDebug(dcDenon) << "Set mute" << mute;
|
||||
QByteArray cmd;
|
||||
if (mute) {
|
||||
cmd = "MUON\r";
|
||||
} else {
|
||||
cmd = "MUOFF\r";
|
||||
}
|
||||
sendCommand(cmd);
|
||||
}
|
||||
|
||||
void AvrConnection::setPower(bool power)
|
||||
{
|
||||
qCDebug(dcDenon) << "Set power" << power;
|
||||
QByteArray cmd;
|
||||
if (power) {
|
||||
cmd = "PWON\r";
|
||||
} else {
|
||||
cmd = "PWSTANDBY\r";
|
||||
}
|
||||
sendCommand(cmd);
|
||||
}
|
||||
|
||||
void AvrConnection::setSurroundMode(const QByteArray &surroundMode)
|
||||
{
|
||||
qCDebug(dcDenon) << "Set surround mode" << surroundMode;
|
||||
QByteArray cmd = "MS" + surroundMode + "\r";
|
||||
sendCommand(cmd);
|
||||
}
|
||||
|
||||
void AvrConnection::increaseVolume()
|
||||
{
|
||||
qCDebug(dcDenon) << "Execute volume increase";
|
||||
QByteArray cmd = "MVUP\r";
|
||||
sendCommand(cmd);
|
||||
}
|
||||
|
||||
void AvrConnection::decreaseVolume()
|
||||
{
|
||||
qCDebug(dcDenon) << "Execute volume decrease";
|
||||
QByteArray cmd = "MVDOWN\r";
|
||||
sendCommand(cmd);
|
||||
}
|
||||
|
||||
void AvrConnection::onConnected()
|
||||
{
|
||||
qCDebug(dcDenon) << "connected successfully to" << hostAddress().toString() << port();
|
||||
emit connectionStatusChanged(true);
|
||||
}
|
||||
|
||||
void AvrConnection::onDisconnected()
|
||||
{
|
||||
qCDebug(dcDenon) << "disconnected from" << hostAddress().toString() << port();
|
||||
emit connectionStatusChanged(false);
|
||||
}
|
||||
|
||||
void AvrConnection::onError(QAbstractSocket::SocketError socketError)
|
||||
{
|
||||
qCWarning(dcDenon) << "socket error:" << socketError << m_socket->errorString();
|
||||
emit socketErrorOccured(socketError);
|
||||
}
|
||||
|
||||
void AvrConnection::readData()
|
||||
{
|
||||
QByteArray data = m_socket->readAll();
|
||||
qCDebug(dcDenon) << "Data received" << data;
|
||||
|
||||
if (data.contains("MV") && !data.contains("MAX")){
|
||||
int index = data.indexOf("MV");
|
||||
int volume = data.mid(index+2, 2).toInt();
|
||||
emit volumeChanged(volume);
|
||||
}
|
||||
|
||||
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";
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
|
||||
* Copyright (C) 2016 Bernhard Trinnes <bernhard.trinnes@guh.guru> *
|
||||
* Copyright (C) 2015 Simon Stürz <simon.stuerz@nymea.io> *
|
||||
* Copyright (C) 2019 Bernhard Trinnes <bernhard.trinnes@nymea.io> *
|
||||
* *
|
||||
* This file is part of nymea. *
|
||||
* *
|
||||
@ -21,36 +21,48 @@
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef DENONCONNECTION_H
|
||||
#define DENONCONNECTION_H
|
||||
#ifndef AVRCONNECTION_H
|
||||
#define AVRCONNECTION_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QTcpSocket>
|
||||
#include <QHostAddress>
|
||||
|
||||
class DenonConnection : public QObject
|
||||
class AvrConnection : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit DenonConnection(const QHostAddress &hostAddress, const int &port = 23, QObject *parent = 0);
|
||||
~DenonConnection();
|
||||
explicit AvrConnection(const QHostAddress &hostAddress, const int &port = 23, QObject *parent = nullptr);
|
||||
~AvrConnection();
|
||||
|
||||
void connectDenon();
|
||||
void disconnectDenon();
|
||||
void connectDevice();
|
||||
void disconnectDevice();
|
||||
|
||||
QHostAddress hostAddress() const;
|
||||
int port() const;
|
||||
|
||||
bool connected();
|
||||
|
||||
void sendData(const QByteArray &message);
|
||||
void getAllStatus();
|
||||
void getChannel();
|
||||
void getVolume();
|
||||
void getMute();
|
||||
void getPower();
|
||||
void getSurroundMode();
|
||||
|
||||
void setChannel(const QByteArray &channel);
|
||||
void setVolume(int volume);
|
||||
void setMute(bool mute);
|
||||
void setPower(bool power);
|
||||
void setSurroundMode(const QByteArray &surroundMode);
|
||||
|
||||
void increaseVolume();
|
||||
void decreaseVolume();
|
||||
private:
|
||||
QTcpSocket *m_socket;
|
||||
|
||||
QTcpSocket *m_socket = nullptr;
|
||||
QHostAddress m_hostAddress;
|
||||
int m_port;
|
||||
bool m_connected;
|
||||
|
||||
void sendCommand(const QByteArray &message);
|
||||
|
||||
private slots:
|
||||
void onConnected();
|
||||
@ -58,13 +70,14 @@ private slots:
|
||||
void onError(QAbstractSocket::SocketError socketError);
|
||||
void readData();
|
||||
|
||||
void setConnected(const bool &connected);
|
||||
|
||||
signals:
|
||||
void socketErrorOccured(QAbstractSocket::SocketError socketError);
|
||||
void connectionStatusChanged();
|
||||
void dataReady(const QByteArray &data);
|
||||
|
||||
void connectionStatusChanged(bool status);
|
||||
void volumeChanged(int volume);
|
||||
void muteChanged(bool mute);
|
||||
void channelChanged(const QByteArray &channel);
|
||||
void powerChanged(bool power);
|
||||
void surroundModeChanged(const QByteArray &surroundMode);
|
||||
};
|
||||
|
||||
#endif // DENONCONNECTION_H
|
||||
#endif // AVRCONNECTION_H
|
||||
@ -6,8 +6,13 @@ TARGET = $$qtLibraryTarget(nymea_deviceplugindenon)
|
||||
|
||||
SOURCES += \
|
||||
deviceplugindenon.cpp \
|
||||
denonconnection.cpp
|
||||
heos.cpp \
|
||||
heosplayer.cpp \
|
||||
avrconnection.cpp
|
||||
|
||||
HEADERS += \
|
||||
deviceplugindenon.h \
|
||||
denonconnection.h
|
||||
heos.h \
|
||||
heosplayer.h \
|
||||
avrconnection.h \
|
||||
heostypes.h
|
||||
|
||||
@ -1,108 +0,0 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
|
||||
* Copyright (C) 2016 Bernhard Trinnes <bernhard.trinnes@guh.io> *
|
||||
* *
|
||||
* This file is part of nymea. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Lesser General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2.1 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; If not, see *
|
||||
* <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "denonconnection.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
DenonConnection::DenonConnection(const QHostAddress &hostAddress, const int &port, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_hostAddress(hostAddress),
|
||||
m_port(port),
|
||||
m_connected(false)
|
||||
{
|
||||
m_socket = new QTcpSocket(this);
|
||||
|
||||
connect(m_socket, &QTcpSocket::connected, this, &DenonConnection::onConnected);
|
||||
connect(m_socket, &QTcpSocket::disconnected, this, &DenonConnection::onDisconnected);
|
||||
connect(m_socket, &QTcpSocket::readyRead, this, &DenonConnection::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)));
|
||||
}
|
||||
|
||||
DenonConnection::~DenonConnection()
|
||||
{
|
||||
m_socket->close();
|
||||
}
|
||||
|
||||
void DenonConnection::connectDenon()
|
||||
{
|
||||
if (m_socket->state() == QAbstractSocket::ConnectingState) {
|
||||
return;
|
||||
}
|
||||
m_socket->connectToHost(m_hostAddress, m_port);
|
||||
}
|
||||
|
||||
void DenonConnection::disconnectDenon()
|
||||
{
|
||||
m_socket->close();
|
||||
}
|
||||
|
||||
QHostAddress DenonConnection::hostAddress() const
|
||||
{
|
||||
return m_hostAddress;
|
||||
}
|
||||
|
||||
int DenonConnection::port() const
|
||||
{
|
||||
return m_port;
|
||||
}
|
||||
|
||||
bool DenonConnection::connected()
|
||||
{
|
||||
return m_connected;
|
||||
}
|
||||
|
||||
void DenonConnection::sendData(const QByteArray &message)
|
||||
{
|
||||
m_socket->write(message);
|
||||
}
|
||||
|
||||
void DenonConnection::onConnected()
|
||||
{
|
||||
qCDebug(dcDenon) << "connected successfully to" << hostAddress().toString() << port();
|
||||
setConnected(true);
|
||||
}
|
||||
|
||||
void DenonConnection::onDisconnected()
|
||||
{
|
||||
qCDebug(dcDenon) << "disconnected from" << hostAddress().toString() << port();
|
||||
setConnected(false);
|
||||
}
|
||||
|
||||
void DenonConnection::onError(QAbstractSocket::SocketError socketError)
|
||||
{
|
||||
qCWarning(dcDenon) << "socket error:" << socketError << m_socket->errorString();
|
||||
emit socketErrorOccured(socketError);
|
||||
}
|
||||
|
||||
void DenonConnection::readData()
|
||||
{
|
||||
QByteArray data = m_socket->readAll();
|
||||
emit dataReady(QString(data).toUtf8());
|
||||
}
|
||||
|
||||
void DenonConnection::setConnected(const bool &connected)
|
||||
{
|
||||
m_connected = connected;
|
||||
emit connectionStatusChanged();
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
|
||||
* Copyright (C) 2016 Bernhard Trinnes <bernhard.trinnes@guh.guru> *
|
||||
* Copyright (C) 2015 Simon Stürz <simon.stuerz@nymea.io> *
|
||||
* Copyright (C) 2019 Bernhard Trinnes <bernhard.trinnes@nymea.io> *
|
||||
* *
|
||||
* This file is part of nymea. *
|
||||
* *
|
||||
@ -21,112 +21,301 @@
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
/*!
|
||||
\page denon.html
|
||||
\title Denon
|
||||
\brief Plugin for Denon AV and Heos Devices
|
||||
|
||||
\ingroup plugins
|
||||
\ingroup nymea-plugins
|
||||
|
||||
This plug-in supports the
|
||||
\l {http://www.denon.de/de/product/hometheater/avreceivers/avrx1000}{Denon AV Amplifier AVR-X1000}
|
||||
|
||||
\chapter Plugin properties
|
||||
Following JSON file contains the definition and the description of all available \l{DeviceClass}{DeviceClasses}
|
||||
and \l{Vendor}{Vendors} of this \l{DevicePlugin}.
|
||||
|
||||
For more details how to read this JSON file please check out the documentation for \l{The plugin JSON File}.
|
||||
|
||||
\quotefile plugins/deviceplugins/denon/deviceplugindenon.json
|
||||
*/
|
||||
|
||||
#include "deviceplugindenon.h"
|
||||
#include "plugininfo.h"
|
||||
#include "devices/device.h"
|
||||
#include "network/networkaccessmanager.h"
|
||||
#include "network/upnp/upnpdiscovery.h"
|
||||
#include "network/upnp/upnpdiscoveryreply.h"
|
||||
#include "platform/platformzeroconfcontroller.h"
|
||||
#include "network/zeroconf/zeroconfservicebrowser.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QStringList>
|
||||
#include <QJsonDocument>
|
||||
#include <QTimer>
|
||||
|
||||
DevicePluginDenon::DevicePluginDenon()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
DevicePluginDenon::~DevicePluginDenon()
|
||||
Device::DeviceError DevicePluginDenon::discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms)
|
||||
{
|
||||
hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
|
||||
}
|
||||
Q_UNUSED(params)
|
||||
|
||||
void DevicePluginDenon::init()
|
||||
{
|
||||
m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(15);
|
||||
connect(m_pluginTimer, &PluginTimer::timeout, this, &DevicePluginDenon::onPluginTimer);
|
||||
if (deviceClassId == AVRX1000DeviceClassId) {
|
||||
if (!m_serviceBrowser) {
|
||||
m_serviceBrowser = hardwareManager()->zeroConfController()->createServiceBrowser();
|
||||
connect(m_serviceBrowser, &ZeroConfServiceBrowser::serviceEntryAdded, this, &DevicePluginDenon::onAvahiServiceEntryAdded);
|
||||
connect(m_serviceBrowser, &ZeroConfServiceBrowser::serviceEntryRemoved, this, &DevicePluginDenon::onAvahiServiceEntryRemoved);
|
||||
}
|
||||
QStringList discoveredIds;
|
||||
|
||||
QList<DeviceDescriptor> deviceDescriptors;
|
||||
foreach (const ZeroConfServiceEntry &service, m_serviceBrowser->serviceEntries()) {
|
||||
if (service.txt().contains("am=AVRX1000")) {
|
||||
|
||||
QString id = service.name().split("@").first();
|
||||
QString name = service.name().split("@").last();
|
||||
QString address = service.hostAddress().toString();
|
||||
qCDebug(dcDenon) << "service discovered" << name << "ID:" << id;
|
||||
if (discoveredIds.contains(id))
|
||||
break;
|
||||
discoveredIds.append(id);
|
||||
DeviceDescriptor deviceDescriptor(AVRX1000DeviceClassId, name, address);
|
||||
ParamList params;
|
||||
params.append(Param(AVRX1000DeviceIpParamTypeId, address));
|
||||
params.append(Param(AVRX1000DeviceIdParamTypeId, id));
|
||||
deviceDescriptor.setParams(params);
|
||||
foreach (Device *existingDevice, myDevices()) {
|
||||
if (existingDevice->paramValue(AVRX1000DeviceIdParamTypeId).toString() == id) {
|
||||
deviceDescriptor.setDeviceId(existingDevice->id());
|
||||
break;
|
||||
}
|
||||
}
|
||||
deviceDescriptors.append(deviceDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
emit devicesDiscovered(AVRX1000DeviceClassId, deviceDescriptors);
|
||||
return Device::DeviceErrorAsync;
|
||||
}
|
||||
|
||||
if (deviceClassId == heosDeviceClassId) {
|
||||
/*
|
||||
* The HEOS products can be discovered using the UPnP SSDP protocol. Through discovery,
|
||||
* the IP address of the HEOS products can be retrieved. Once the IP address is retrieved,
|
||||
* a telnet connection to port 1255 can be opened to access the HEOS CLI and control the HEOS system.
|
||||
* The HEOS product IP address can also be set statically and manually programmed into the control system.
|
||||
* Search target name (ST) in M-SEARCH discovery request is 'urn:schemas-denon-com:device:ACT-Denon:1'.
|
||||
*/
|
||||
UpnpDiscoveryReply *reply = hardwareManager()->upnpDiscovery()->discoverDevices();
|
||||
connect(reply, &UpnpDiscoveryReply::finished, this, &DevicePluginDenon::onUpnpDiscoveryFinished);
|
||||
return Device::DeviceErrorAsync;
|
||||
}
|
||||
return Device::DeviceErrorDeviceClassNotFound;
|
||||
}
|
||||
|
||||
Device::DeviceSetupStatus DevicePluginDenon::setupDevice(Device *device)
|
||||
{
|
||||
qCDebug(dcDenon) << "Setup Denon device" << device->paramValue(AVRX1000DeviceIpParamTypeId).toString();
|
||||
|
||||
// Check if we already have a denon device
|
||||
if (!myDevices().isEmpty()) {
|
||||
qCWarning(dcDenon) << "Could not add denon device. Only one denon device allowed.";
|
||||
return Device::DeviceSetupStatusFailure;
|
||||
if(!m_pluginTimer) {
|
||||
m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(60);
|
||||
connect(m_pluginTimer, &PluginTimer::timeout, this, &DevicePluginDenon::onPluginTimer);
|
||||
}
|
||||
|
||||
QHostAddress address(device->paramValue(AVRX1000DeviceIpParamTypeId).toString());
|
||||
if (address.isNull()) {
|
||||
qCWarning(dcDenon) << "Could not parse ip address" << device->paramValue(AVRX1000DeviceIpParamTypeId).toString();
|
||||
return Device::DeviceSetupStatusFailure;
|
||||
if (device->deviceClassId() == AVRX1000DeviceClassId) {
|
||||
qCDebug(dcDenon) << "Setup Denon device" << device->paramValue(AVRX1000DeviceIpParamTypeId).toString();
|
||||
|
||||
QHostAddress address(device->paramValue(AVRX1000DeviceIpParamTypeId).toString());
|
||||
if (address.isNull()) {
|
||||
qCWarning(dcDenon) << "Could not parse ip address" << device->paramValue(AVRX1000DeviceIpParamTypeId).toString();
|
||||
return Device::DeviceSetupStatusFailure;
|
||||
}
|
||||
|
||||
AvrConnection *denonConnection = new AvrConnection(address, 23, this);
|
||||
connect(denonConnection, &AvrConnection::connectionStatusChanged, this, &DevicePluginDenon::onAvrConnectionChanged);
|
||||
connect(denonConnection, &AvrConnection::socketErrorOccured, this, &DevicePluginDenon::onAvrSocketError);
|
||||
connect(denonConnection, &AvrConnection::channelChanged, this, &DevicePluginDenon::onAvrChannelChanged);
|
||||
connect(denonConnection, &AvrConnection::powerChanged, this, &DevicePluginDenon::onAvrPowerChanged);
|
||||
connect(denonConnection, &AvrConnection::volumeChanged, this, &DevicePluginDenon::onAvrVolumeChanged);
|
||||
connect(denonConnection, &AvrConnection::surroundModeChanged, this, &DevicePluginDenon::onAvrSurroundModeChanged);
|
||||
connect(denonConnection, &AvrConnection::muteChanged, this, &DevicePluginDenon::onAvrMuteChanged);
|
||||
|
||||
m_asyncAvrSetups.append(denonConnection);
|
||||
denonConnection->connectDevice();
|
||||
m_avrConnections.insert(device, denonConnection);
|
||||
return Device::DeviceSetupStatusAsync;
|
||||
}
|
||||
|
||||
m_device = device;
|
||||
m_denonConnection = new DenonConnection(address, 23, this);
|
||||
connect(m_denonConnection.data(), &DenonConnection::connectionStatusChanged, this, &DevicePluginDenon::onConnectionChanged);
|
||||
connect(m_denonConnection.data(), &DenonConnection::socketErrorOccured, this, &DevicePluginDenon::onSocketError);
|
||||
connect(m_denonConnection.data(), &DenonConnection::dataReady, this, &DevicePluginDenon::onDataReceived);
|
||||
if (device->deviceClassId() == heosDeviceClassId) {
|
||||
qCDebug(dcDenon) << "Setup Denon device" << device->paramValue(heosDeviceIpParamTypeId).toString();
|
||||
|
||||
m_asyncSetups.append(m_denonConnection);
|
||||
m_denonConnection->connectDenon();
|
||||
QHostAddress address(device->paramValue(heosDeviceIpParamTypeId).toString());
|
||||
Heos *heos = new Heos(address, this);
|
||||
connect(heos, &Heos::connectionStatusChanged, this, &DevicePluginDenon::onHeosConnectionChanged);
|
||||
connect(heos, &Heos::playerDiscovered, this, &DevicePluginDenon::onHeosPlayerDiscovered);
|
||||
connect(heos, &Heos::playStateReceived, this, &DevicePluginDenon::onHeosPlayStateReceived);
|
||||
connect(heos, &Heos::repeatModeReceived, this, &DevicePluginDenon::onHeosRepeatModeReceived);
|
||||
connect(heos, &Heos::shuffleModeReceived, this, &DevicePluginDenon::onHeosShuffleModeReceived);
|
||||
connect(heos, &Heos::muteStatusReceived, this, &DevicePluginDenon::onHeosMuteStatusReceived);
|
||||
connect(heos, &Heos::volumeStatusReceived, this, &DevicePluginDenon::onHeosVolumeStatusReceived);
|
||||
connect(heos, &Heos::nowPlayingMediaStatusReceived, this, &DevicePluginDenon::onHeosNowPlayingMediaStatusReceived);
|
||||
|
||||
return Device::DeviceSetupStatusAsync;
|
||||
m_asyncHeosSetups.append(heos);
|
||||
heos->connectHeos();
|
||||
m_heos.insert(device, heos);
|
||||
return Device::DeviceSetupStatusAsync;
|
||||
}
|
||||
|
||||
if (device->deviceClassId() == heosPlayerDeviceClassId) {
|
||||
return Device::DeviceSetupStatusSuccess;
|
||||
}
|
||||
return Device::DeviceSetupStatusFailure;
|
||||
}
|
||||
|
||||
void DevicePluginDenon::deviceRemoved(Device *device)
|
||||
{
|
||||
qCDebug(dcDenon) << "Delete " << device->name();
|
||||
if (m_denonConnection.isNull()){
|
||||
qCWarning(dcDenon) << "Invalid connection pointer" << device->id().toString();
|
||||
return;
|
||||
|
||||
if (device->deviceClassId() == AVRX1000DeviceClassId) {
|
||||
AvrConnection *denonConnection = m_avrConnections.value(device);
|
||||
m_avrConnections.remove(device);
|
||||
denonConnection->disconnectDevice();
|
||||
denonConnection->deleteLater();
|
||||
}
|
||||
|
||||
if (device->deviceClassId() == heosDeviceClassId) {
|
||||
if (m_avrConnections.contains(device)) {
|
||||
AvrConnection *denonConnection = m_avrConnections.value(device);
|
||||
m_avrConnections.remove(device);
|
||||
denonConnection->disconnectDevice();
|
||||
denonConnection->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
if (myDevices().empty()) {
|
||||
hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
|
||||
}
|
||||
m_device.clear();
|
||||
m_denonConnection->disconnectDenon();
|
||||
m_denonConnection->deleteLater();
|
||||
}
|
||||
|
||||
Device::DeviceError DevicePluginDenon::executeAction(Device *device, const Action &action)
|
||||
{
|
||||
qCDebug(dcDenon) << "Execute action" << device->id() << action.id() << action.params();
|
||||
if (device->deviceClassId() == AVRX1000DeviceClassId) {
|
||||
AvrConnection *avrConnection = m_avrConnections.value(device);
|
||||
|
||||
// check connection state
|
||||
if (m_denonConnection.isNull() || !m_denonConnection->connected())
|
||||
return Device::DeviceErrorHardwareNotAvailable;
|
||||
|
||||
// check if the requested action is our "update" action ...
|
||||
if (action.actionTypeId() == AVRX1000PowerActionTypeId) {
|
||||
|
||||
// Print information that we are executing now the update action
|
||||
qCDebug(dcDenon) << "set power action" << action.id();
|
||||
qCDebug(dcDenon) << "power: " << action.param(AVRX1000PowerActionPowerParamTypeId).value().Bool;
|
||||
|
||||
if (action.param(AVRX1000PowerActionPowerParamTypeId).value().toBool() == true){
|
||||
QByteArray cmd = "PWON\r";
|
||||
qCDebug(dcDenon) << "Execute power: " << action.id() << cmd;
|
||||
m_denonConnection->sendData(cmd);
|
||||
} else {
|
||||
QByteArray cmd = "PWSTANDBY\r";
|
||||
qCDebug(dcDenon) << "Execute power: " << action.id() << cmd;
|
||||
m_denonConnection->sendData(cmd);
|
||||
}
|
||||
|
||||
bool power = action.param(AVRX1000PowerActionPowerParamTypeId).value().toBool();
|
||||
avrConnection->setPower(power);
|
||||
return Device::DeviceErrorNoError;
|
||||
|
||||
} else if (action.actionTypeId() == AVRX1000VolumeActionTypeId) {
|
||||
|
||||
QByteArray vol = action.param(AVRX1000VolumeActionVolumeParamTypeId).value().toByteArray();
|
||||
QByteArray cmd = "MV" + vol + "\r";
|
||||
|
||||
qCDebug(dcDenon) << "Execute volume" << action.id() << cmd;
|
||||
m_denonConnection->sendData(cmd);
|
||||
|
||||
int vol = action.param(AVRX1000VolumeActionVolumeParamTypeId).value().toInt();
|
||||
avrConnection->setVolume(vol);
|
||||
return Device::DeviceErrorNoError;
|
||||
|
||||
} else if (action.actionTypeId() == AVRX1000ChannelActionTypeId) {
|
||||
|
||||
qCDebug(dcDenon) << "Execute update action" << action.id();
|
||||
QByteArray channel = action.param(AVRX1000ChannelActionChannelParamTypeId).value().toByteArray();
|
||||
QByteArray cmd = "SI" + channel + "\r";
|
||||
avrConnection->setChannel(channel);
|
||||
return Device::DeviceErrorNoError;
|
||||
|
||||
qCDebug(dcDenon) << "Change to channel:" << cmd;
|
||||
m_denonConnection->sendData(cmd);
|
||||
} else if (action.actionTypeId() == AVRX1000IncreaseVolumeActionTypeId) {
|
||||
|
||||
avrConnection->increaseVolume();
|
||||
return Device::DeviceErrorNoError;
|
||||
|
||||
} else if (action.actionTypeId() == AVRX1000DecreaseVolumeActionTypeId) {
|
||||
|
||||
avrConnection->decreaseVolume();
|
||||
return Device::DeviceErrorNoError;
|
||||
|
||||
} else if (action.actionTypeId() == AVRX1000SurroundModeActionTypeId) {
|
||||
|
||||
QByteArray surroundMode = action.param(AVRX1000SurroundModeActionSurroundModeParamTypeId).value().toByteArray();
|
||||
avrConnection->setSurroundMode(surroundMode);
|
||||
return Device::DeviceErrorNoError;
|
||||
}
|
||||
return Device::DeviceErrorActionTypeNotFound;
|
||||
}
|
||||
|
||||
if (device->deviceClassId() == heosPlayerDeviceClassId) {
|
||||
|
||||
Device *heosDevice = myDevices().findById(device->parentId());
|
||||
Heos *heos = m_heos.value(heosDevice);
|
||||
int playerId = device->paramValue(heosPlayerDevicePlayerIdParamTypeId).toInt();
|
||||
|
||||
if (action.actionTypeId() == heosPlayerVolumeActionTypeId) {
|
||||
int volume = action.param(heosPlayerVolumeActionVolumeParamTypeId).value().toInt();
|
||||
heos->setVolume(playerId, volume);
|
||||
return Device::DeviceErrorNoError;
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == heosPlayerMuteActionTypeId) {
|
||||
bool mute = action.param(heosPlayerMuteActionMuteParamTypeId).value().toBool();
|
||||
heos->setMute(playerId, mute);
|
||||
return Device::DeviceErrorNoError;
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == heosPlayerPlaybackStatusActionTypeId) {
|
||||
QString playbackStatus = action.param(heosPlayerPlaybackStatusActionPlaybackStatusParamTypeId).value().toString();
|
||||
if (playbackStatus == "playing") {
|
||||
heos->setPlayerState(playerId, PLAYER_STATE_PLAY);
|
||||
} else if (playbackStatus == "stopping") {
|
||||
heos->setPlayerState(playerId, PLAYER_STATE_STOP);
|
||||
} else if (playbackStatus == "pausing") {
|
||||
heos->setPlayerState(playerId, PLAYER_STATE_PAUSE);
|
||||
}
|
||||
return Device::DeviceErrorNoError;
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == heosPlayerShuffleActionTypeId) {
|
||||
bool shuffle = action.param(heosPlayerShuffleActionShuffleParamTypeId).value().toBool();
|
||||
REPEAT_MODE repeatMode = REPEAT_MODE_OFF;
|
||||
if ( device->stateValue(heosPlayerRepeatStateTypeId) == "One") {
|
||||
repeatMode = REPEAT_MODE_ONE;
|
||||
} else if ( device->stateValue(heosPlayerRepeatStateTypeId) == "All") {
|
||||
repeatMode = REPEAT_MODE_ALL;
|
||||
}
|
||||
heos->setPlayMode(playerId, repeatMode, shuffle);
|
||||
return Device::DeviceErrorNoError;
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == heosPlayerSkipBackActionTypeId) {
|
||||
heos->playPrevious(playerId);
|
||||
return Device::DeviceErrorNoError;
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == heosPlayerFastRewindActionTypeId) {
|
||||
|
||||
return Device::DeviceErrorActionTypeNotFound;
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == heosPlayerStopActionTypeId) {
|
||||
heos->setPlayerState(playerId, PLAYER_STATE_STOP);
|
||||
return Device::DeviceErrorNoError;
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == heosPlayerPlayActionTypeId) {
|
||||
heos->setPlayerState(playerId, PLAYER_STATE_PLAY);
|
||||
return Device::DeviceErrorNoError;
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == heosPlayerPauseActionTypeId) {
|
||||
heos->setPlayerState(playerId, PLAYER_STATE_PAUSE);
|
||||
return Device::DeviceErrorNoError;
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == heosPlayerFastForwardActionTypeId) {
|
||||
|
||||
return Device::DeviceErrorActionTypeNotFound;
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == heosPlayerSkipNextActionTypeId) {
|
||||
heos->playNext(playerId);
|
||||
return Device::DeviceErrorNoError;
|
||||
}
|
||||
return Device::DeviceErrorActionTypeNotFound;
|
||||
@ -134,125 +323,407 @@ Device::DeviceError DevicePluginDenon::executeAction(Device *device, const Actio
|
||||
return Device::DeviceErrorDeviceClassNotFound;
|
||||
}
|
||||
|
||||
void DevicePluginDenon::postSetupDevice(Device *device)
|
||||
{
|
||||
if (device->deviceClassId() == heosDeviceClassId) {
|
||||
|
||||
Heos *heos = m_heos.value(device);
|
||||
heos->getPlayers();
|
||||
}
|
||||
|
||||
if (device->deviceClassId() == heosPlayerDeviceClassId) {
|
||||
device->setStateValue(heosPlayerConnectedStateTypeId, true);
|
||||
Device *heosDevice = myDevices().findById(device->parentId());
|
||||
Heos *heos = m_heos.value(heosDevice);
|
||||
int playerId = device->paramValue(heosPlayerDevicePlayerIdParamTypeId).toInt();
|
||||
heos->getPlayerState(playerId);
|
||||
heos->getPlayMode(playerId);
|
||||
heos->getVolume(playerId);
|
||||
heos->getMute(playerId);
|
||||
heos->getNowPlayingMedia(playerId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DevicePluginDenon::onPluginTimer()
|
||||
{
|
||||
if (m_denonConnection.isNull())
|
||||
return;
|
||||
|
||||
if (!m_denonConnection->connected()) {
|
||||
m_denonConnection->connectDenon();
|
||||
} else {
|
||||
m_denonConnection->sendData("PW?\rSI?\rMV?\r");
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginDenon::onConnectionChanged()
|
||||
{
|
||||
if (!m_device)
|
||||
return;
|
||||
|
||||
// if the device is connected
|
||||
if (m_denonConnection->connected()) {
|
||||
// and from the first setup
|
||||
if (m_asyncSetups.contains(m_denonConnection)) {
|
||||
m_asyncSetups.removeAll(m_denonConnection);
|
||||
m_denonConnection->sendData("PW?\rSI?\rMV?\r");
|
||||
emit deviceSetupFinished(m_device, Device::DeviceSetupStatusSuccess);
|
||||
foreach(AvrConnection *denonConnection, m_avrConnections.values()) {
|
||||
if (!denonConnection->connected()) {
|
||||
denonConnection->connectDevice();
|
||||
}
|
||||
Device *device = m_avrConnections.key(denonConnection);
|
||||
if (device->deviceClassId() == AVRX1000DeviceClassId) {
|
||||
denonConnection->getAllStatus();
|
||||
}
|
||||
}
|
||||
|
||||
// Set connection status
|
||||
m_device->setStateValue(AVRX1000ConnectedStateTypeId, m_denonConnection->connected());
|
||||
}
|
||||
foreach(Device *device, myDevices()) {
|
||||
|
||||
void DevicePluginDenon::onDataReceived(const QByteArray &data)
|
||||
{
|
||||
qCDebug(dcDenon) << "Data received" << data;
|
||||
|
||||
// if there is no device, return
|
||||
if (m_device.isNull())
|
||||
return;
|
||||
|
||||
if (data.contains("MV") && !data.contains("MAX")){
|
||||
int index = data.indexOf("MV");
|
||||
int vol = data.mid(index+2, 2).toInt();
|
||||
|
||||
qCDebug(dcDenon) << "Update volume:" << vol;
|
||||
m_device->setStateValue(AVRX1000VolumeStateTypeId, vol);
|
||||
}
|
||||
|
||||
if (data.contains("SI")) {
|
||||
QString 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";
|
||||
if (device->deviceClassId() == heosDeviceClassId) {
|
||||
Heos *heos = m_heos.value(device);
|
||||
heos->getPlayers();
|
||||
heos->registerForChangeEvents(true);
|
||||
}
|
||||
|
||||
qCDebug(dcDenon) << "Update channel:" << cmd;
|
||||
m_device->setStateValue(AVRX1000ChannelStateTypeId, cmd);
|
||||
}
|
||||
if (device->deviceClassId() == heosPlayerDeviceClassId) {
|
||||
Device *heosDevice = myDevices().findById(device->parentId());
|
||||
Heos *heos = m_heos.value(heosDevice);
|
||||
int playerId = device->paramValue(heosPlayerDevicePlayerIdParamTypeId).toInt();
|
||||
|
||||
if (data.contains("PWON")) {
|
||||
qCDebug(dcDenon) << "Update power on";
|
||||
m_device->setStateValue(AVRX1000PowerStateTypeId, true);
|
||||
} else if (data.contains("PWSTANDBY")) {
|
||||
qCDebug(dcDenon) << "Update power off";
|
||||
m_device->setStateValue(AVRX1000PowerStateTypeId, false);
|
||||
heos->getPlayerState(playerId);
|
||||
heos->getPlayMode(playerId);
|
||||
heos->getVolume(playerId);
|
||||
heos->getMute(playerId);
|
||||
heos->getNowPlayingMedia(playerId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginDenon::onSocketError()
|
||||
void DevicePluginDenon::onAvrConnectionChanged(bool status)
|
||||
{
|
||||
// if there is no device, return
|
||||
if (m_device.isNull())
|
||||
AvrConnection *denonConnection = static_cast<AvrConnection *>(sender());
|
||||
Device *device = m_avrConnections.key(denonConnection);
|
||||
if (!device)
|
||||
return;
|
||||
|
||||
// Check if setup running for this device
|
||||
if (m_asyncSetups.contains(m_denonConnection)) {
|
||||
qCWarning(dcDenon()) << "Could not add device. The setup failed.";
|
||||
emit deviceSetupFinished(m_device, Device::DeviceSetupStatusFailure);
|
||||
// Delete the connection, the device will not be added and
|
||||
// the connection will be created in the next setup
|
||||
m_denonConnection->deleteLater();
|
||||
if (device->deviceClassId() == AVRX1000DeviceClassId) {
|
||||
// if the device is connected
|
||||
if (status) {
|
||||
// and from the first setup
|
||||
if (m_asyncAvrSetups.contains(denonConnection)) {
|
||||
m_asyncAvrSetups.removeAll(denonConnection);
|
||||
|
||||
emit deviceSetupFinished(device, Device::DeviceSetupStatusSuccess);
|
||||
}
|
||||
}
|
||||
device->setStateValue(AVRX1000ConnectedStateTypeId, denonConnection->connected());
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginDenon::onAvrVolumeChanged(int volume)
|
||||
{
|
||||
AvrConnection *denonConnection = static_cast<AvrConnection *>(sender());
|
||||
Device *device = m_avrConnections.key(denonConnection);
|
||||
if (!device)
|
||||
return;
|
||||
|
||||
if (device->deviceClassId() == AVRX1000DeviceClassId) {
|
||||
device->setStateValue(AVRX1000VolumeStateTypeId, volume);
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginDenon::onAvrChannelChanged(const QByteArray &channel)
|
||||
{
|
||||
AvrConnection *denonConnection = static_cast<AvrConnection *>(sender());
|
||||
Device *device = m_avrConnections.key(denonConnection);
|
||||
if (!device)
|
||||
return;
|
||||
|
||||
if (device->deviceClassId() == AVRX1000DeviceClassId) {
|
||||
device->setStateValue(AVRX1000ChannelStateTypeId, channel);
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginDenon::onAvrMuteChanged(bool mute)
|
||||
{
|
||||
AvrConnection *denonConnection = static_cast<AvrConnection *>(sender());
|
||||
Device *device = m_avrConnections.key(denonConnection);
|
||||
if (!device)
|
||||
return;
|
||||
|
||||
if (device->deviceClassId() == AVRX1000DeviceClassId) {
|
||||
device->setStateValue(AVRX1000MuteStateTypeId, mute);
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginDenon::onAvrPowerChanged(bool power)
|
||||
{
|
||||
AvrConnection *denonConnection = static_cast<AvrConnection *>(sender());
|
||||
Device *device = m_avrConnections.key(denonConnection);
|
||||
if (!device)
|
||||
return;
|
||||
|
||||
if (device->deviceClassId() == AVRX1000DeviceClassId) {
|
||||
device->setStateValue(AVRX1000PowerStateTypeId, power);
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginDenon::onAvrSurroundModeChanged(const QByteArray &surroundMode)
|
||||
{
|
||||
AvrConnection *denonConnection = static_cast<AvrConnection *>(sender());
|
||||
Device *device = m_avrConnections.key(denonConnection);
|
||||
if (!device)
|
||||
return;
|
||||
|
||||
if (device->deviceClassId() == AVRX1000DeviceClassId) {
|
||||
device->setStateValue(AVRX1000SurroundModeStateTypeId, surroundMode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DevicePluginDenon::onAvrSocketError()
|
||||
{
|
||||
AvrConnection *denonConnection = static_cast<AvrConnection *>(sender());
|
||||
Device *device = m_avrConnections.key(denonConnection);
|
||||
if (!device)
|
||||
return;
|
||||
|
||||
if (device->deviceClassId() == AVRX1000DeviceClassId) {
|
||||
|
||||
// Check if setup running for this device
|
||||
if (m_asyncAvrSetups.contains(denonConnection)) {
|
||||
m_asyncAvrSetups.removeAll(denonConnection);
|
||||
qCWarning(dcDenon()) << "Could not add device. The setup failed.";
|
||||
emit deviceSetupFinished(device, Device::DeviceSetupStatusFailure);
|
||||
// Delete the connection, the device will not be added and
|
||||
// the connection will be created in the next setup
|
||||
denonConnection->deleteLater();
|
||||
m_avrConnections.remove(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginDenon::onUpnpDiscoveryFinished()
|
||||
{
|
||||
qCDebug(dcDenon()) << "Upnp discovery finished";
|
||||
UpnpDiscoveryReply *reply = static_cast<UpnpDiscoveryReply *>(sender());
|
||||
if (reply->error() != UpnpDiscoveryReply::UpnpDiscoveryReplyErrorNoError) {
|
||||
qCWarning(dcDenon()) << "Upnp discovery error" << reply->error();
|
||||
}
|
||||
reply->deleteLater();
|
||||
|
||||
if (reply->deviceDescriptors().isEmpty()) {
|
||||
qCDebug(dcDenon) << "No UPnP device found.";
|
||||
return;
|
||||
}
|
||||
|
||||
QList<DeviceDescriptor> heosDescriptors;
|
||||
foreach (const UpnpDeviceDescriptor &upnpDevice, reply->deviceDescriptors()) {
|
||||
|
||||
if (upnpDevice.modelName().contains("HEOS")) {
|
||||
QString serialNumber = upnpDevice.serialNumber();
|
||||
if (serialNumber != "0000001") {
|
||||
// child devices have serial number 0000001
|
||||
qCDebug(dcDenon) << "UPnP device found:" << upnpDevice.modelDescription() << upnpDevice.friendlyName() << upnpDevice.hostAddress().toString() << upnpDevice.modelName() << upnpDevice.manufacturer() << upnpDevice.serialNumber();
|
||||
DeviceDescriptor descriptor(heosDeviceClassId, upnpDevice.modelName(), serialNumber);
|
||||
ParamList params;
|
||||
foreach (Device *existingDevice, myDevices()) {
|
||||
if (existingDevice->paramValue(heosDeviceSerialNumberParamTypeId).toString().contains(serialNumber, Qt::CaseSensitivity::CaseInsensitive)) {
|
||||
descriptor.setDeviceId(existingDevice->id());
|
||||
break;
|
||||
}
|
||||
}
|
||||
params.append(Param(heosDeviceModelNameParamTypeId, upnpDevice.modelName()));
|
||||
params.append(Param(heosDeviceIpParamTypeId, upnpDevice.hostAddress().toString()));
|
||||
params.append(Param(heosDeviceSerialNumberParamTypeId, serialNumber));
|
||||
descriptor.setParams(params);
|
||||
heosDescriptors.append(descriptor);
|
||||
}
|
||||
}
|
||||
qCDebug(dcDenon) << "UPnP device found:" << upnpDevice.modelDescription() << upnpDevice.friendlyName() << upnpDevice.hostAddress().toString() << upnpDevice.modelName() << upnpDevice.manufacturer() << upnpDevice.serialNumber();
|
||||
}
|
||||
if (!heosDescriptors.isEmpty()) {
|
||||
emit devicesDiscovered(heosDeviceClassId, heosDescriptors);
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginDenon::onHeosConnectionChanged(bool status)
|
||||
{
|
||||
Heos *heos = static_cast<Heos *>(sender());
|
||||
heos->registerForChangeEvents(true);
|
||||
Device *device = m_heos.key(heos);
|
||||
if (!device)
|
||||
return;
|
||||
|
||||
if (device->deviceClassId() == heosDeviceClassId) {
|
||||
// if the device is connected
|
||||
if (status) {
|
||||
// and from the first setup
|
||||
if (m_asyncHeosSetups.contains(heos)) {
|
||||
m_asyncHeosSetups.removeAll(heos);
|
||||
heos->getPlayers();
|
||||
emit deviceSetupFinished(device, Device::DeviceSetupStatusSuccess);
|
||||
}
|
||||
}
|
||||
device->setStateValue(heosConnectedStateTypeId, status);
|
||||
// update connection status for all child devices
|
||||
foreach (Device *playerDevice, myDevices()) {
|
||||
if (playerDevice->deviceClassId() == heosPlayerDeviceClassId) {
|
||||
if (playerDevice->parentId() == device->id()) {
|
||||
playerDevice->setStateValue(heosPlayerConnectedStateTypeId, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginDenon::onHeosPlayerDiscovered(HeosPlayer *heosPlayer) {
|
||||
|
||||
Heos *heos = static_cast<Heos *>(sender());
|
||||
Device *device = m_heos.key(heos);
|
||||
|
||||
foreach (Device *heosPlayerDevice, myDevices()) {
|
||||
if(heosPlayerDevice->deviceClassId() == heosPlayerDeviceClassId) {
|
||||
if (heosPlayerDevice->paramValue(heosPlayerDevicePlayerIdParamTypeId).toInt() == heosPlayer->playerId())
|
||||
return;
|
||||
}
|
||||
}
|
||||
QList<DeviceDescriptor> heosPlayerDescriptors;
|
||||
DeviceDescriptor descriptor(heosPlayerDeviceClassId, heosPlayer->name(), heosPlayer->playerModel(), device->id());
|
||||
ParamList params;
|
||||
params.append(Param(heosPlayerDeviceModelParamTypeId, heosPlayer->playerModel()));
|
||||
params.append(Param(heosPlayerDevicePlayerIdParamTypeId, heosPlayer->playerId()));
|
||||
params.append(Param(heosPlayerDeviceSerialNumberParamTypeId, heosPlayer->serialNumber()));
|
||||
params.append(Param(heosPlayerDeviceVersionParamTypeId, heosPlayer->playerVersion()));
|
||||
descriptor.setParams(params);
|
||||
qCDebug(dcDenon) << "Found new heos player" << heosPlayer->name();
|
||||
heosPlayerDescriptors.append(descriptor);
|
||||
autoDevicesAppeared(heosPlayerDeviceClassId, heosPlayerDescriptors);
|
||||
}
|
||||
|
||||
void DevicePluginDenon::onHeosPlayStateReceived(int playerId, PLAYER_STATE state)
|
||||
{
|
||||
foreach(Device *device, myDevices().filterByParam(heosPlayerDevicePlayerIdParamTypeId, playerId)) {
|
||||
if (state == PLAYER_STATE_PAUSE) {
|
||||
device->setStateValue(heosPlayerPlaybackStatusStateTypeId, "Paused");
|
||||
} else if (state == PLAYER_STATE_PLAY) {
|
||||
device->setStateValue(heosPlayerPlaybackStatusStateTypeId, "Playing");
|
||||
} else if (state == PLAYER_STATE_STOP) {
|
||||
device->setStateValue(heosPlayerPlaybackStatusStateTypeId, "Stopped");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DevicePluginDenon::onHeosRepeatModeReceived(int playerId, REPEAT_MODE repeatMode)
|
||||
{
|
||||
foreach(Device *device, myDevices().filterByParam(heosPlayerDevicePlayerIdParamTypeId, playerId)) {
|
||||
if (repeatMode == REPEAT_MODE_ALL) {
|
||||
device->setStateValue(heosPlayerRepeatStateTypeId, "All");
|
||||
} else if (repeatMode == REPEAT_MODE_ONE) {
|
||||
device->setStateValue(heosPlayerRepeatStateTypeId, "One");
|
||||
} else if (repeatMode == REPEAT_MODE_OFF) {
|
||||
device->setStateValue(heosPlayerRepeatStateTypeId, "None");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginDenon::onHeosShuffleModeReceived(int playerId, bool shuffle)
|
||||
{
|
||||
foreach(Device *device, myDevices().filterByParam(heosPlayerDevicePlayerIdParamTypeId, playerId)) {
|
||||
device->setStateValue(heosPlayerMuteStateTypeId, shuffle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginDenon::onHeosMuteStatusReceived(int playerId, bool mute)
|
||||
{
|
||||
foreach(Device *device, myDevices().filterByParam(heosPlayerDevicePlayerIdParamTypeId, playerId)) {
|
||||
device->setStateValue(heosPlayerMuteStateTypeId, mute);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginDenon::onHeosVolumeStatusReceived(int playerId, int volume)
|
||||
{
|
||||
foreach(Device *device, myDevices().filterByParam(heosPlayerDevicePlayerIdParamTypeId, playerId)) {
|
||||
device->setStateValue(heosPlayerVolumeStateTypeId, volume);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginDenon::onHeosNowPlayingMediaStatusReceived(int playerId, SOURCE_ID sourceId, QString artist, QString album, QString song, QString artwork)
|
||||
{
|
||||
foreach(Device *device, myDevices().filterByParam(heosPlayerDevicePlayerIdParamTypeId, playerId)) {
|
||||
device->setStateValue(heosPlayerArtistStateTypeId, artist);
|
||||
device->setStateValue(heosPlayerTitleStateTypeId, song);
|
||||
device->setStateValue(heosPlayerArtworkStateTypeId, artwork);
|
||||
device->setStateValue(heosPlayerCollectionStateTypeId, album);
|
||||
QString source;
|
||||
switch (sourceId) {
|
||||
case SOURCE_ID_PANDORA:
|
||||
source = "Pandora";
|
||||
break;
|
||||
case SOURCE_ID_RHAPSODY:
|
||||
source = "Rhapsody";
|
||||
break;
|
||||
case SOURCE_ID_TUNEIN:
|
||||
source = "TuneIn";
|
||||
break;
|
||||
case SOURCE_ID_SPOTIFY:
|
||||
source = "Spotify";
|
||||
break;
|
||||
case SOURCE_ID_DEEZER:
|
||||
source = "Deezer";
|
||||
break;
|
||||
case SOURCE_ID_NAPSTER:
|
||||
source = "Napster";
|
||||
break;
|
||||
case SOURCE_ID_IHEARTRADIO:
|
||||
source = "iHeartRadio";
|
||||
break;
|
||||
case SOURCE_ID_SIRIUS_XM:
|
||||
source = "Sirius XM";
|
||||
break;
|
||||
case SOURCE_ID_SOUNDCLOUD:
|
||||
source = "Soundcloud";
|
||||
break;
|
||||
case SOURCE_ID_TIDAL:
|
||||
source = "Tidal";
|
||||
break;
|
||||
case SOURCE_ID_FUTURE_SERVICE_1:
|
||||
source = "Unknown";
|
||||
break;
|
||||
case SOURCE_ID_RDIO:
|
||||
source = "Rdio";
|
||||
break;
|
||||
case SOURCE_ID_AMAZON_MUSIC:
|
||||
source = "Amazon Music";
|
||||
break;
|
||||
case SOURCE_ID_FUTURE_SERVICE_2:
|
||||
source = "Unknown";
|
||||
break;
|
||||
case SOURCE_ID_MOODMIX:
|
||||
source = "Moodmix";
|
||||
break;
|
||||
case SOURCE_ID_JUKE:
|
||||
source = "Juke";
|
||||
break;
|
||||
case SOURCE_ID_FUTURE_SERVICE_3:
|
||||
source = "Unkown";
|
||||
break;
|
||||
case SOURCE_ID_QQMUSIC:
|
||||
source = "QQMusic";
|
||||
break;
|
||||
case SOURCE_ID_LOCAL_MEDIA:
|
||||
source = "USB Media/DLNA Servers";
|
||||
break;
|
||||
case SOURCE_ID_HEOS_PLAYLIST:
|
||||
source = "HEOS Playlists";
|
||||
break;
|
||||
case SOURCE_ID_HEOS_HISTORY:
|
||||
source = "HEOS History";
|
||||
break;
|
||||
case SOURCE_ID_HEOS_FAVORITES:
|
||||
source = "HEOS Favorites";
|
||||
break;
|
||||
case SOURCE_ID_HEOS_AUX:
|
||||
source = "HEOS aux input";
|
||||
break;
|
||||
};
|
||||
device->setStateValue(heosPlayerSourceStateTypeId, source);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginDenon::onAvahiServiceEntryAdded(const ZeroConfServiceEntry &serviceEntry)
|
||||
{
|
||||
qCDebug(dcDenon()) << "Avahi service entry added:" << serviceEntry;
|
||||
}
|
||||
|
||||
void DevicePluginDenon::onAvahiServiceEntryRemoved(const ZeroConfServiceEntry &serviceEntry)
|
||||
{
|
||||
qCDebug(dcDenon()) << "Avahi service entry removed:" << serviceEntry;
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
|
||||
* Copyright (C) 2016 Bernhard Trinnes <bernhard.trinnes@guh.guru> *
|
||||
* Copyright (C) 2015 Simon Stürz <simon.stuerz@nymea.io> *
|
||||
* Copyright (C) 2019 Bernhard Trinnes <bernhard.trinnes@nymea.io> *
|
||||
* *
|
||||
* This file is part of nymea. *
|
||||
* *
|
||||
@ -24,18 +24,21 @@
|
||||
#ifndef DEVICEPLUGINDENON_H
|
||||
#define DEVICEPLUGINDENON_H
|
||||
|
||||
#include "heos.h"
|
||||
#include "avrconnection.h"
|
||||
#include "plugintimer.h"
|
||||
#include "devices/deviceplugin.h"
|
||||
#include "network/zeroconf/zeroconfservicebrowser.h"
|
||||
#include "network/zeroconf/zeroconfserviceentry.h"
|
||||
|
||||
|
||||
#include <QProcess>
|
||||
#include <QPair>
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
#include <QHostAddress>
|
||||
#include <QNetworkReply>
|
||||
|
||||
#include "plugintimer.h"
|
||||
#include "denonconnection.h"
|
||||
|
||||
class DevicePluginDenon : public DevicePlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -45,27 +48,50 @@ class DevicePluginDenon : public DevicePlugin
|
||||
|
||||
public:
|
||||
explicit DevicePluginDenon();
|
||||
~DevicePluginDenon();
|
||||
|
||||
void init() override;
|
||||
Device::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override;
|
||||
Device::DeviceSetupStatus setupDevice(Device *device) override;
|
||||
void deviceRemoved(Device *device) override;
|
||||
void postSetupDevice(Device * device) override;
|
||||
Device::DeviceError executeAction(Device *device, const Action &action) override;
|
||||
void deviceRemoved(Device *device) override;
|
||||
|
||||
private:
|
||||
PluginTimer *m_pluginTimer = nullptr;
|
||||
QPointer<Device> m_device;
|
||||
QPointer<DenonConnection> m_denonConnection;
|
||||
QList<DenonConnection *> m_asyncSetups;
|
||||
ZeroConfServiceBrowser *m_serviceBrowser = nullptr;
|
||||
|
||||
QHash<Device *, AvrConnection*> m_avrConnections;
|
||||
QHash<Device *, Heos*> m_heos;
|
||||
|
||||
QList<AvrConnection *> m_asyncAvrSetups;
|
||||
QList<Heos *> m_asyncHeosSetups;
|
||||
|
||||
QHash<int, Device *> m_playerIds;
|
||||
QHash<int, Device *> m_discoveredPlayerIds;
|
||||
QHash<const Action *, int> m_asyncActions;
|
||||
|
||||
QHash <ActionId, Device *> m_asyncActions;
|
||||
QHash <QNetworkReply *, ActionId> m_asyncActionReplies;
|
||||
|
||||
private slots:
|
||||
void onPluginTimer();
|
||||
void onConnectionChanged();
|
||||
void onDataReceived(const QByteArray &data);
|
||||
void onSocketError();
|
||||
void onUpnpDiscoveryFinished();
|
||||
|
||||
void onHeosConnectionChanged(bool status);
|
||||
void onHeosPlayerDiscovered(HeosPlayer *heosPlayer);
|
||||
void onHeosPlayStateReceived(int playerId, PLAYER_STATE state);
|
||||
void onHeosShuffleModeReceived(int playerId, bool shuffle);
|
||||
void onHeosRepeatModeReceived(int playerId, REPEAT_MODE repeatMode);
|
||||
void onHeosMuteStatusReceived(int playerId, bool mute);
|
||||
void onHeosVolumeStatusReceived(int playerId, int volume);
|
||||
void onHeosNowPlayingMediaStatusReceived(int playerId, SOURCE_ID source, QString artist, QString album, QString Song, QString artwork);
|
||||
|
||||
void onAvahiServiceEntryAdded(const ZeroConfServiceEntry &serviceEntry);
|
||||
void onAvahiServiceEntryRemoved(const ZeroConfServiceEntry &serviceEntry);
|
||||
void onAvrConnectionChanged(bool status);
|
||||
void onAvrSocketError();
|
||||
void onAvrVolumeChanged(int volume);
|
||||
void onAvrChannelChanged(const QByteArray &channel);
|
||||
void onAvrMuteChanged(bool mute);
|
||||
void onAvrPowerChanged(bool power);
|
||||
void onAvrSurroundModeChanged(const QByteArray &surroundMode);
|
||||
};
|
||||
|
||||
#endif // DEVICEPLUGINDENON_H
|
||||
|
||||
@ -12,14 +12,21 @@
|
||||
"id": "1cd3d67e-aba0-450e-9e2a-483a1527aba6",
|
||||
"name": "AVRX1000",
|
||||
"displayName": "AVR X1000",
|
||||
"createMethods": ["user"],
|
||||
"createMethods": ["discovery"],
|
||||
"interfaces": ["extendedvolumecontroller", "connectable", "power"],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "a54b98b4-b78f-41dd-a257-14425c6cf9ab",
|
||||
"name": "ip",
|
||||
"displayName": "ip",
|
||||
"displayName": "Ip",
|
||||
"type" : "QString",
|
||||
"inputType": "IPv4Address"
|
||||
},
|
||||
{
|
||||
"id": "2e8806cb-f6f3-4e9a-b6ea-0b35f75e61c5",
|
||||
"name": "id",
|
||||
"displayName": "Id",
|
||||
"type" : "QString"
|
||||
}
|
||||
],
|
||||
"stateTypes": [
|
||||
@ -42,16 +49,25 @@
|
||||
"writable": true
|
||||
},
|
||||
{
|
||||
"displayName": "volume",
|
||||
"displayName": "Mute",
|
||||
"id": "3e11470d-a5b7-499c-be55-9b1b4fe5eedf",
|
||||
"name": "mute",
|
||||
"displayNameEvent": "Mute changed",
|
||||
"displayNameAction": "Set mute",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"writable": true
|
||||
},
|
||||
{
|
||||
"displayName": "Volume",
|
||||
"id": "773636b9-304d-463a-8755-fc7488dc0ff3",
|
||||
"name": "volume",
|
||||
"displayNameEvent": "volume changed",
|
||||
"displayNameEvent": "Volume changed",
|
||||
"displayNameAction": "Set volume",
|
||||
"type": "int",
|
||||
"unit": "Dezibel",
|
||||
"defaultValue": 0,
|
||||
"minValue": 0,
|
||||
"maxValue": 80,
|
||||
"maxValue": 100,
|
||||
"writable": true
|
||||
},
|
||||
{
|
||||
@ -84,6 +100,277 @@
|
||||
"FVP"
|
||||
],
|
||||
"defaultValue": "TUNER"
|
||||
},
|
||||
{
|
||||
"displayName": "Surround mode",
|
||||
"id": "4f203bdd-691c-4384-a934-2d49a5448f0a",
|
||||
"name": "surroundMode",
|
||||
"displayNameEvent": "Surround mode changed",
|
||||
"displayNameAction": "Set surround mode",
|
||||
"type": "QString",
|
||||
"writable": true,
|
||||
"possibleValues": [
|
||||
"MOVIE",
|
||||
"MUSIC",
|
||||
"GAME",
|
||||
"PURE DIRECT",
|
||||
"DIRECT",
|
||||
"STEREO",
|
||||
"STANDARD",
|
||||
"DOLBY DIGITAL",
|
||||
"DTS SUROUND",
|
||||
"MCH STEREO",
|
||||
"ROCK ARENA",
|
||||
"JAZZ CLUB",
|
||||
"MONO MOVIE",
|
||||
"MATRIX"
|
||||
],
|
||||
"defaultValue": "MOVIE"
|
||||
}
|
||||
],
|
||||
"actionTypes": [
|
||||
{
|
||||
"id": "4ae686d6-2307-40a0-bd38-2cd3a92342cc",
|
||||
"displayName": "Increase volume",
|
||||
"name": "increaseVolume",
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "765c7e2a-9eb6-46fc-a880-4e96c81f8d1e",
|
||||
"displayName": "Step",
|
||||
"name": "step",
|
||||
"type": "int"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "d3752c32-92e3-4396-8e2f-ab5e57c6cfb1",
|
||||
"displayName": "Decrease volume",
|
||||
"name": "decreaseVolume",
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "765c7e2a-9eb6-46fc-a880-4e96c81f8d1e",
|
||||
"displayName": "Step",
|
||||
"name": "step",
|
||||
"type": "int"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "28bbf4c6-dfd8-4d9d-aa27-5daf2c25d63c",
|
||||
"name": "heos",
|
||||
"displayName": "Heos",
|
||||
"createMethods": ["discovery"],
|
||||
"interfaces": ["gateway"],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "a54b98b4-b78f-41dd-a257-14425c6cf9ab",
|
||||
"name": "ip",
|
||||
"displayName": "IPv4 address",
|
||||
"type" : "QString",
|
||||
"inputType": "IPv4Address"
|
||||
},
|
||||
{
|
||||
"id": "f796664d-6cb7-4f29-9d05-771968d82a32",
|
||||
"name": "serialNumber",
|
||||
"displayName": "Serial number",
|
||||
"type" : "QString"
|
||||
},
|
||||
{
|
||||
"id": "ab1a0be8-e3a5-4f95-b9b7-893de1ca4cf7",
|
||||
"name": "modelName",
|
||||
"displayName": "Model name",
|
||||
"type" : "QString"
|
||||
}
|
||||
],
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "4d1790bf-28c6-4c1f-8892-ba1a0ef140f5",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"displayNameEvent": "Connected changed",
|
||||
"defaultValue": false,
|
||||
"type": "bool"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "fce5247f-4c6d-408f-ac62-e5973dc6adfa",
|
||||
"name": "heosPlayer",
|
||||
"displayName": "Heos player",
|
||||
"createMethods": ["auto"],
|
||||
"interfaces": ["extendedmediacontroller", "extendedvolumecontroller", "mediametadataprovider", "shufflerepeat", "connectable"],
|
||||
"paramTypes":[
|
||||
{
|
||||
"id": "89629008-6ad8-4e92-863d-b86e0e012d0b",
|
||||
"name": "playerId",
|
||||
"displayName": "Player ID",
|
||||
"type" : "int"
|
||||
},
|
||||
{
|
||||
"id": "e760f92b-8fca-4f20-aead-a52045505b81",
|
||||
"name": "model",
|
||||
"displayName": "Model",
|
||||
"type" : "QString"
|
||||
},
|
||||
{
|
||||
"id": "aa1158f7-b451-456a-840f-4f0c63b2b7f0",
|
||||
"name": "version",
|
||||
"displayName": "Version",
|
||||
"type" : "QString"
|
||||
},
|
||||
{
|
||||
"id": "f796664d-6cb7-4f29-9d05-771968d82a32",
|
||||
"name": "serialNumber",
|
||||
"displayName": "Serial number",
|
||||
"type" : "QString"
|
||||
}
|
||||
],
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "9a4e527e-057c-4b19-8a02-605cc8349f5e",
|
||||
"name": "connected",
|
||||
"displayName": "connected",
|
||||
"displayNameEvent": "Connected changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "fcc89c7c-b793-4b6f-a3dc-0e0e3a86748f",
|
||||
"name": "mute",
|
||||
"displayName": "Mute",
|
||||
"displayNameEvent": "Mute changed",
|
||||
"displayNameAction": "Set mute",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"cached": false,
|
||||
"writable": true
|
||||
},
|
||||
{
|
||||
"id": "6d4886a1-fa5d-4889-96c5-7a1c206f59be",
|
||||
"name": "volume",
|
||||
"displayName": "Volume",
|
||||
"displayNameEvent": "volume changed",
|
||||
"displayNameAction": "set volume",
|
||||
"type": "int",
|
||||
"defaultValue": 50,
|
||||
"minValue": 0,
|
||||
"maxValue": 100,
|
||||
"writable": true
|
||||
},
|
||||
{
|
||||
"id": "6db3b484-4cd4-477b-b822-275865d308db",
|
||||
"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": "4b581237-acf5-4d8f-9e83-9b24e9ac900a",
|
||||
"name": "shuffle",
|
||||
"displayName": "shuffle",
|
||||
"displayNameEvent": "shuffle changed",
|
||||
"displayNameAction": "set shuffle",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"cached": false,
|
||||
"writable": true
|
||||
},
|
||||
{
|
||||
"id": "4e60cd17-5845-4351-aa2c-2504610e1532",
|
||||
"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
|
||||
},
|
||||
{
|
||||
"id": "eee22722-3ee5-48f7-8af8-275dc04b21eb",
|
||||
"name": "source",
|
||||
"displayName": "source",
|
||||
"displayNameEvent": "source changed",
|
||||
"type": "QString",
|
||||
"defaultValue": ""
|
||||
},
|
||||
{
|
||||
"id": "0a9183a4-b633-4773-ba7a-f4266895157e",
|
||||
"name": "artist",
|
||||
"displayName": "artist",
|
||||
"displayNameEvent": "artist changed",
|
||||
"type": "QString",
|
||||
"defaultValue": ""
|
||||
},
|
||||
{
|
||||
"id": "9cd60864-f141-4e03-a85b-357690cad1b8",
|
||||
"name": "collection",
|
||||
"displayName": "album",
|
||||
"displayNameEvent": "album changed",
|
||||
"type": "QString",
|
||||
"defaultValue": ""
|
||||
},
|
||||
{
|
||||
"id": "bbeecf30-6feb-48d5-ade3-57b2a4eea05f",
|
||||
"name": "title",
|
||||
"displayName": "title",
|
||||
"displayNameEvent": "title changed",
|
||||
"type": "QString",
|
||||
"defaultValue": ""
|
||||
},
|
||||
{
|
||||
"id": "a7f0ba95-383a-4efd-adc5-a36e50a04018",
|
||||
"name": "artwork",
|
||||
"displayName": "artwork",
|
||||
"displayNameEvent": "artwork changed",
|
||||
"type": "QString",
|
||||
"defaultValue": ""
|
||||
}
|
||||
],
|
||||
"actionTypes": [
|
||||
{
|
||||
"id": "a718f7e9-0b54-4403-b661-49f7b0d13085",
|
||||
"name": "skipBack",
|
||||
"displayName": "skip back"
|
||||
},
|
||||
{
|
||||
"id": "fe42d89f-aaad-4f33-a022-d80bdf3a7b19",
|
||||
"name": "fastRewind",
|
||||
"displayName": "fast rewind"
|
||||
},
|
||||
{
|
||||
"id": "c4b29c09-e3b3-4843-b6d9-e032f3fc1d78",
|
||||
"name": "stop",
|
||||
"displayName": "stop"
|
||||
},
|
||||
{
|
||||
"id": "c64964e4-cea0-468a-a9bf-8f69657b74e9",
|
||||
"name": "play",
|
||||
"displayName": "play"
|
||||
},
|
||||
{
|
||||
"id": "21c1cbe6-278f-4688-a65f-6620be1ee5ea",
|
||||
"name": "pause",
|
||||
"displayName": "pause"
|
||||
},
|
||||
{
|
||||
"id": "60b62e88-c68b-463f-b328-2c5d67a71ca0",
|
||||
"name": "fastForward",
|
||||
"displayName": "fast forward"
|
||||
},
|
||||
{
|
||||
"id": "57697e9c-ce5e-4b8f-b42e-16662829ceb2",
|
||||
"name": "skipNext",
|
||||
"displayName": "skip next"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
588
denon/heos.cpp
Normal file
588
denon/heos.cpp
Normal file
@ -0,0 +1,588 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2019 Bernhard Trinnes <bernhard.trinnes@guh.io> *
|
||||
* *
|
||||
* This file is part of nymea. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Lesser General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2.1 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; If not, see *
|
||||
* <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "heos.h"
|
||||
#include "extern-plugininfo.h"
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonDocument>
|
||||
#include <QUrlQuery>
|
||||
#include <QTimer>
|
||||
|
||||
Heos::Heos(const QHostAddress &hostAddress, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_hostAddress(hostAddress)
|
||||
{
|
||||
m_socket = new QTcpSocket(this);
|
||||
|
||||
connect(m_socket, &QTcpSocket::connected, this, &Heos::onConnected);
|
||||
connect(m_socket, &QTcpSocket::disconnected, this, &Heos::onDisconnected);
|
||||
connect(m_socket, &QTcpSocket::readyRead, this, &Heos::readData);
|
||||
connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onError(QAbstractSocket::SocketError)));
|
||||
}
|
||||
|
||||
Heos::~Heos()
|
||||
{
|
||||
m_socket->close();
|
||||
}
|
||||
|
||||
void Heos::connectHeos()
|
||||
{
|
||||
if (m_socket->state() == QAbstractSocket::ConnectingState) {
|
||||
return;
|
||||
}
|
||||
m_socket->connectToHost(m_hostAddress, 1255);
|
||||
}
|
||||
|
||||
/*
|
||||
* SYSTEM COMMANDS
|
||||
*/
|
||||
void Heos::registerForChangeEvents(bool state)
|
||||
{
|
||||
QByteArray query;
|
||||
|
||||
if (state) {
|
||||
query = "?enable=on";
|
||||
} else {
|
||||
query = "?enable=off";
|
||||
}
|
||||
QByteArray cmd = "heos://system/register_for_change_events" + query + "\r\n";
|
||||
qCDebug(dcDenon) << "Register for change events:" << cmd;
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::sendHeartbeat()
|
||||
{
|
||||
QByteArray cmd = "heos://system/heart_beat\r\n";
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::getUserAccount()
|
||||
{
|
||||
QByteArray cmd = "heos://system/check_account\r\n";
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::setUserAccount(QString userName, QString password)
|
||||
{
|
||||
QByteArray cmd = "heos://system/sign_in?un=" + userName.toLocal8Bit() + "&pw=" + password.toLocal8Bit() + "\r\n";
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::logoutUserAccount()
|
||||
{
|
||||
QByteArray cmd = "heos://system/sign_out\r\n";
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::rebootSpeaker()
|
||||
{
|
||||
QByteArray cmd = "heos://system/reboot\r\n";
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::prettifyJsonResponse(bool enable)
|
||||
{
|
||||
QByteArray cmd = "heos://system/prettify_json_response?enable=";
|
||||
if (enable) {
|
||||
cmd.append("on=\r\n");
|
||||
} else {
|
||||
cmd.append("off=\r\n");
|
||||
}
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* PLAYER COMMANDS
|
||||
*/
|
||||
|
||||
void Heos::getNowPlayingMedia(int playerId)
|
||||
{
|
||||
QByteArray cmd = "heos://player/get_now_playing_media?pid=" + QVariant(playerId).toByteArray() + "\r\n";
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
HeosPlayer *Heos::getPlayer(int playerId)
|
||||
{
|
||||
return m_heosPlayers.value(playerId);
|
||||
}
|
||||
|
||||
void Heos::getPlayers()
|
||||
{
|
||||
QByteArray cmd = "heos://player/get_players\r\n";
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::getVolume(int playerId)
|
||||
{
|
||||
QByteArray cmd = "heos://player/get_volume?pid=" + QVariant(playerId).toByteArray() + "\r\n";
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::setVolume(int playerId, int volume)
|
||||
{
|
||||
QByteArray cmd = "heos://player/set_volume?pid=" + QVariant(playerId).toByteArray() + "&level=" + QVariant(volume).toByteArray() + "\r\n";
|
||||
qCDebug(dcDenon) << "Set volume:" << cmd;
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::getMute(int playerId)
|
||||
{
|
||||
QByteArray cmd = "heos://player/get_mute?pid=" + QVariant(playerId).toByteArray() + "\r\n";
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::setMute(int playerId, bool state)
|
||||
{
|
||||
QByteArray stateQuery;
|
||||
if(state) {
|
||||
stateQuery = "&state=on";
|
||||
} else {
|
||||
stateQuery = "&state=off";
|
||||
}
|
||||
QByteArray cmd = "heos://player/set_mute?pid=" + QVariant(playerId).toByteArray() + stateQuery + "\r\n";
|
||||
qCDebug(dcDenon) << "Set mute:" << cmd;
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::setPlayerState(int playerId, PLAYER_STATE state)
|
||||
{
|
||||
QByteArray playerStateQuery;
|
||||
|
||||
if (state == PLAYER_STATE_PLAY){
|
||||
playerStateQuery = "&state=play";
|
||||
} else if (state == PLAYER_STATE_PAUSE){
|
||||
playerStateQuery = "&state=pause";
|
||||
} else if (state == PLAYER_STATE_STOP){
|
||||
playerStateQuery = "&state=stop";
|
||||
}
|
||||
|
||||
QByteArray cmd = "heos://player/set_play_state?pid=" + QVariant(playerId).toByteArray() + playerStateQuery + "\r\n";
|
||||
qCDebug(dcDenon) << "Set play mode:" << cmd;
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::getPlayerState(int playerId)
|
||||
{
|
||||
QByteArray cmd = "heos://player/get_play_state?pid=" + QVariant(playerId).toByteArray() + "\r\n";
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
|
||||
void Heos::setPlayMode(int playerId, REPEAT_MODE repeatMode, bool shuffle)
|
||||
{
|
||||
QByteArray repeatModeQuery;
|
||||
|
||||
if (repeatMode == REPEAT_MODE_OFF) {
|
||||
repeatModeQuery = "&repeat=off";
|
||||
} else if (repeatMode == REPEAT_MODE_ONE) {
|
||||
repeatModeQuery = "&repeat=on_one";
|
||||
} else if (repeatMode == REPEAT_MODE_ALL) {
|
||||
repeatModeQuery = "&repeat=on_all";
|
||||
}
|
||||
|
||||
QByteArray shuffleQuery;
|
||||
if (shuffle) {
|
||||
shuffleQuery = "&shuffle=on";
|
||||
} else {
|
||||
shuffleQuery = "&shuffle=off";
|
||||
}
|
||||
|
||||
QByteArray cmd = "heos://player/set_play_mode?pid=" + QVariant(playerId).toByteArray() + repeatModeQuery + shuffleQuery + "\r\n";
|
||||
qCDebug(dcDenon) << "Set play mode:" << cmd;
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::getPlayMode(int playerId)
|
||||
{
|
||||
QByteArray cmd = "heos://player/get_play_mode?pid=" + QVariant(playerId).toByteArray() + "\r\n";
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::getQueue(int playerId)
|
||||
{
|
||||
QByteArray cmd = "heos://player/get_queue?pid=" + QVariant(playerId).toByteArray() + "\r\n";
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
/*
|
||||
* GROUP COMMANDS
|
||||
*/
|
||||
void Heos::getGroups()
|
||||
{
|
||||
QByteArray cmd = "heos://group/get_groups\r\n";
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::getGroupInfo(int groupId)
|
||||
{
|
||||
QByteArray cmd = "heos://group/get_group_info?gid=" + QVariant(groupId).toByteArray() + "\r\n";
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::getGroupVolume(int groupId)
|
||||
{
|
||||
QByteArray cmd = "heos://group/get_volume?gid=" + QVariant(groupId).toByteArray() + "\r\n";
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::getGroupMute(int groupId)
|
||||
{
|
||||
QByteArray cmd = "heos://group/get_mute?gid=" + QVariant(groupId).toByteArray() + "\r\n";
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::playNext(int playerId)
|
||||
{
|
||||
QByteArray cmd = "heos://player/play_next?pid=" + QVariant(playerId).toByteArray() + "\r\n";
|
||||
qCDebug(dcDenon) << "Play next:" << cmd;
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::playPrevious(int playerId)
|
||||
{
|
||||
QByteArray cmd = "heos://player/play_previous?pid=" + QVariant(playerId).toByteArray() + "\r\n";
|
||||
qCDebug(dcDenon) << "Play previous:" << cmd;
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::volumeUp(int playerId, int step)
|
||||
{
|
||||
QByteArray cmd = "heos://player/volume_up?pid=" + QVariant(playerId).toByteArray() + "&step=" + QVariant(step).toByteArray() + "\r\n";
|
||||
qCDebug(dcDenon) << "Volume up:" << cmd;
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::volumeDown(int playerId, int step)
|
||||
{
|
||||
QByteArray cmd = "heos://player/volume_down?pid=" + QVariant(playerId).toByteArray() + "&step=" + QVariant(step).toByteArray() + "\r\n";
|
||||
qCDebug(dcDenon) << "Volume down:" << cmd;
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::clearQueue(int playerId)
|
||||
{
|
||||
QByteArray cmd = "heos://player/clear_queue?pid=" + QVariant(playerId).toByteArray() + "\r\n";
|
||||
qCDebug(dcDenon) << "clear queue:" << cmd;
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::moveQueue(int playerId, int sourcQueueId, int destinationQueueId)
|
||||
{
|
||||
QUrl url("player");
|
||||
url.setScheme("heos");
|
||||
url.setPath("move_queue_item");
|
||||
url.setQuery(QString("pid=%1").arg(playerId));
|
||||
url.setQuery(QString("sqid=%1").arg(sourcQueueId));
|
||||
url.setQuery(QString("dqid=%1").arg(destinationQueueId));
|
||||
qCDebug(dcDenon) << "moving queue:" << url;
|
||||
m_socket->write(url.toEncoded());
|
||||
}
|
||||
|
||||
void Heos::checkForFirmwareUpdate(int playerId)
|
||||
{
|
||||
QByteArray cmd = "heos://player/check_update?pid=" + QVariant(playerId).toByteArray() + "\r\n";
|
||||
qCDebug(dcDenon) << "Check firmware update:" << cmd;
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::setGroupVolume(int groupId, bool volume)
|
||||
{
|
||||
QByteArray cmd = "heos://group/set_volume?gid=" + QVariant(groupId).toByteArray() + "&level=" + QVariant(volume).toByteArray() + "\r\n";
|
||||
qCDebug(dcDenon) << "Volume up:" << cmd;
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::setGroupMute(int groupId, bool mute)
|
||||
{
|
||||
QByteArray cmd = "heos://group/set_mute?gid=" + QVariant(groupId).toByteArray() + "&state=";
|
||||
if (mute) {
|
||||
cmd.append("on\r\n");
|
||||
} else {
|
||||
cmd.append("off\r\n");
|
||||
}
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::toggleGroupMute(int groupId)
|
||||
{
|
||||
QByteArray cmd = "heos://group/toggle_mute?gid=" + QVariant(groupId).toByteArray() + "\r\n";
|
||||
qCDebug(dcDenon) << "Volume up:" << cmd;
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::groupVolumeUp(int groupId, int step)
|
||||
{
|
||||
QByteArray cmd = "heos://group/volume_up?pid=" + QVariant(groupId).toByteArray() + "&step=" + QVariant(step).toByteArray() + "\r\n";
|
||||
qCDebug(dcDenon) << "Group volume up:" << cmd;
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::groupVolumeDown(int groupId, int step)
|
||||
{
|
||||
QByteArray cmd = "heos://group/volume_down?pid=" + QVariant(groupId).toByteArray() + "&step=" + QVariant(step).toByteArray() + "\r\n";
|
||||
qCDebug(dcDenon) << "Group volume up:" << cmd;
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::getMusicSources()
|
||||
{
|
||||
QByteArray cmd = "heos://browse/get_music_sources\r\n";
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::getSourceInfo(SOURCE_ID sourceId)
|
||||
{
|
||||
QByteArray cmd = " heos://browse/get_source_info?sid=" + QVariant(sourceId).toByteArray() + "\r\n";
|
||||
qCDebug(dcDenon) << "Group volume up:" << cmd;
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::getSearchCriteria(SOURCE_ID sourceId)
|
||||
{
|
||||
QByteArray cmd = "heos://browse/get_search_criteria?sid=" + QVariant(sourceId).toByteArray() + "\r\n";
|
||||
qCDebug(dcDenon) << "Group volume up:" << cmd;
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
void Heos::browseSource(SOURCE_ID sourceId)
|
||||
{
|
||||
QByteArray cmd = "heos://browse/browse?sid=" + QVariant(sourceId).toByteArray() + "\r\n";
|
||||
qCDebug(dcDenon) << "Group volume up:" << cmd;
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
/* This command is used to perform the following actions:
|
||||
* Create new group: Creates new group. First player id in the list is group leader.
|
||||
* Adds or delete players from the group. First player id should be the group leader id.
|
||||
* Ungroup all players in the group
|
||||
* Ungroup players. Player id (pid) should be the group leader id.
|
||||
*/
|
||||
//void Heos::setGroup()
|
||||
//{
|
||||
//}
|
||||
|
||||
|
||||
|
||||
void Heos::onConnected()
|
||||
{
|
||||
qCDebug(dcDenon()) << "connected successfully to" << m_hostAddress.toString();
|
||||
emit connectionStatusChanged(true);
|
||||
}
|
||||
|
||||
void Heos::onDisconnected()
|
||||
{
|
||||
qCDebug(dcDenon()) << "Disconnected from" << m_hostAddress.toString() << "try reconnecting in 5 seconds";
|
||||
QTimer::singleShot(5000, this, [this](){
|
||||
connectHeos();
|
||||
});
|
||||
emit connectionStatusChanged(false);
|
||||
}
|
||||
|
||||
void Heos::onError(QAbstractSocket::SocketError socketError)
|
||||
{
|
||||
qCWarning(dcDenon) << "socket error:" << socketError << m_socket->errorString();
|
||||
}
|
||||
|
||||
void Heos::readData()
|
||||
{
|
||||
int playerId = 0;
|
||||
QByteArray data;
|
||||
QJsonParseError error;
|
||||
|
||||
while (m_socket->canReadLine()) {
|
||||
data = m_socket->readLine();
|
||||
//qDebug(dcDenon) << data;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
qCWarning(dcDenon) << "failed to parse json :" << error.errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
QVariantMap dataMap = jsonDoc.toVariant().toMap();
|
||||
if (dataMap.contains("heos")) {
|
||||
QString command = dataMap.value("heos").toMap().value("command").toString();
|
||||
if (command.contains("register_for_change_events")) {
|
||||
QString enabled = dataMap.value("heos").toMap().value("message").toString();
|
||||
if (enabled.contains("off")) {
|
||||
qDebug(dcDenon) << "Events are disabled";
|
||||
m_eventRegistered = false;
|
||||
} else {
|
||||
qDebug(dcDenon) << "Events are enabled";
|
||||
m_eventRegistered = true;
|
||||
}
|
||||
|
||||
} else if (command.contains("get_players")) {
|
||||
QVariantList payloadVariantList = jsonDoc.toVariant().toMap().value("payload").toList();
|
||||
|
||||
foreach (const QVariant &payloadEntryVariant, payloadVariantList) {
|
||||
playerId = payloadEntryVariant.toMap().value("pid").toInt();
|
||||
if(!m_heosPlayers.contains(playerId)){
|
||||
QString serialNumber = payloadEntryVariant.toMap().value("serial").toString();
|
||||
QString name = payloadEntryVariant.toMap().value("name").toString();
|
||||
HeosPlayer *heosPlayer = new HeosPlayer(playerId, name, serialNumber, this);
|
||||
m_heosPlayers.insert(playerId, heosPlayer);
|
||||
emit playerDiscovered(heosPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
QUrlQuery message(dataMap.value("heos").toMap().value("message").toString());
|
||||
if (message.hasQueryItem("pid")) {
|
||||
playerId = message.queryItemValue("pid").toInt();
|
||||
}
|
||||
|
||||
if (command.contains("get_player_info")) {
|
||||
//update heos player info
|
||||
}
|
||||
|
||||
if (command.contains("get_now_playing_media")) {
|
||||
|
||||
QString artist = dataMap.value("payload").toMap().value("artist").toString();
|
||||
QString song = dataMap.value("payload").toMap().value("song").toString();
|
||||
QString artwork = dataMap.value("payload").toMap().value("image_url").toString();
|
||||
QString album = dataMap.value("payload").toMap().value("album").toString();
|
||||
SOURCE_ID sourceId = SOURCE_ID(dataMap.value("payload").toMap().value("sid").toInt());
|
||||
emit nowPlayingMediaStatusReceived(playerId, sourceId, artist, album, song, artwork);
|
||||
}
|
||||
|
||||
if (command.contains("get_play_state") || command.contains("set_play_state")) {
|
||||
if (message.hasQueryItem("state")) {
|
||||
PLAYER_STATE playState = PLAYER_STATE_STOP;
|
||||
if (message.queryItemValue("state").contains("play")) {
|
||||
playState = PLAYER_STATE_PLAY;
|
||||
} else if (message.queryItemValue("state").contains("pause")) {
|
||||
playState = PLAYER_STATE_PAUSE;
|
||||
} else if (message.queryItemValue("state").contains("stop")) {
|
||||
playState = PLAYER_STATE_STOP;
|
||||
}
|
||||
emit playStateReceived(playerId, playState);
|
||||
}
|
||||
}
|
||||
|
||||
if (command.contains("get_volume") || command.contains("set_volume")) {
|
||||
if (message.hasQueryItem("level")) {
|
||||
int volume = message.queryItemValue("level").toInt();
|
||||
emit volumeStatusReceived(playerId, volume);
|
||||
}
|
||||
}
|
||||
|
||||
if (command.contains("get_mute") || command.contains("set_mute")) {
|
||||
if (message.hasQueryItem("state")) {
|
||||
QString state = message.queryItemValue("state");
|
||||
if (state.contains("on")) {
|
||||
emit muteStatusReceived(playerId, true);
|
||||
} else {
|
||||
emit muteStatusReceived(playerId, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (command.contains("get_play_mode") || command.contains("set_play_mode")) {
|
||||
if (message.hasQueryItem("shuffle") && message.hasQueryItem("repeat")) {
|
||||
bool shuffle;
|
||||
if (message.queryItemValue("shuffle").contains("on")){
|
||||
shuffle = true;
|
||||
} else {
|
||||
shuffle = false;
|
||||
}
|
||||
emit shuffleModeReceived(playerId, shuffle);
|
||||
|
||||
REPEAT_MODE repeatMode = REPEAT_MODE_OFF;
|
||||
if (message.queryItemValue("repeat").contains("on_all")){
|
||||
repeatMode = REPEAT_MODE_ALL;
|
||||
} else if (message.queryItemValue("repeat").contains("on_one")){
|
||||
repeatMode = REPEAT_MODE_ONE;
|
||||
} else if (message.queryItemValue("repeat").contains("off")){
|
||||
repeatMode = REPEAT_MODE_OFF;
|
||||
}
|
||||
emit repeatModeReceived(playerId, repeatMode);
|
||||
}
|
||||
}
|
||||
|
||||
if (command.contains("player_state_changed")) {
|
||||
if (message.hasQueryItem("state")) {
|
||||
PLAYER_STATE playState = PLAYER_STATE_STOP;
|
||||
if (message.queryItemValue("state").contains("play")) {
|
||||
playState = PLAYER_STATE_PLAY;
|
||||
} else if (message.queryItemValue("state").contains("pause")) {
|
||||
playState = PLAYER_STATE_PAUSE;
|
||||
} else if (message.queryItemValue("state").contains("stop")) {
|
||||
playState = PLAYER_STATE_STOP;
|
||||
}
|
||||
emit playStateReceived(playerId, playState);
|
||||
}
|
||||
}
|
||||
|
||||
if (command.contains("player_volume_changed")) {
|
||||
qDebug() << "Volume Changed";
|
||||
if (message.hasQueryItem("level")) {
|
||||
int volume = message.queryItemValue("level").toInt();
|
||||
emit volumeStatusReceived(playerId, volume);
|
||||
}
|
||||
if (message.hasQueryItem("mute")) {
|
||||
bool mute;
|
||||
if (message.queryItemValue("mute").contains("on")) {
|
||||
mute = true;
|
||||
} else {
|
||||
mute = false;
|
||||
}
|
||||
emit muteStatusReceived(playerId, mute);
|
||||
}
|
||||
}
|
||||
|
||||
if (command.contains("repeat_mode_changed")) {
|
||||
|
||||
if (message.hasQueryItem("repeat")) {
|
||||
REPEAT_MODE repeatMode = REPEAT_MODE_OFF;
|
||||
if (message.queryItemValue("repeat").contains("on_all")){
|
||||
repeatMode = REPEAT_MODE_ALL;
|
||||
} else if (message.queryItemValue("repeat").contains("on_one")){
|
||||
repeatMode = REPEAT_MODE_ONE;
|
||||
} else if (message.queryItemValue("repeat").contains("off")){
|
||||
repeatMode = REPEAT_MODE_OFF;
|
||||
}
|
||||
emit repeatModeReceived(playerId, repeatMode);
|
||||
}
|
||||
}
|
||||
|
||||
if (command.contains("shuffle_mode_changed")) {
|
||||
|
||||
if (message.hasQueryItem("shuffle")) {
|
||||
bool shuffle;
|
||||
if (message.queryItemValue("shuffle").contains("on")){
|
||||
shuffle = true;
|
||||
} else {
|
||||
shuffle = false;
|
||||
}
|
||||
emit shuffleModeReceived(playerId, shuffle);
|
||||
}
|
||||
}
|
||||
|
||||
if (command.contains("player_now_playing_changed")) {
|
||||
getNowPlayingMedia(playerId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
124
denon/heos.h
Normal file
124
denon/heos.h
Normal file
@ -0,0 +1,124 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2019 Bernhard Trinnes <bernhard.trinnes@nymea.io> *
|
||||
* *
|
||||
* This file is part of nymea. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Lesser General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2.1 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; If not, see *
|
||||
* <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef HEOS_H
|
||||
#define HEOS_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QHostAddress>
|
||||
#include <QTcpSocket>
|
||||
|
||||
#include "heosplayer.h"
|
||||
#include "heostypes.h"
|
||||
|
||||
class Heos : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
explicit Heos(const QHostAddress &hostAddress, QObject *parent = nullptr);
|
||||
~Heos();
|
||||
|
||||
void connectHeos();
|
||||
void setAddress(QHostAddress address);
|
||||
QHostAddress getAddress();
|
||||
HeosPlayer *getPlayer(int playerId);
|
||||
|
||||
// Heos system commands
|
||||
void registerForChangeEvents(bool state); //By default HEOS speaker does not send Change events. Controller needs to send this command with enable=on when it is ready to receive unsolicit responses from CLI. Please refer to "Driver Initialization" section regarding when to register for change events.
|
||||
void sendHeartbeat();
|
||||
void getUserAccount(); //returns current user name in its message field if the user is currently singed in.
|
||||
void setUserAccount(QString userName, QString password);
|
||||
void logoutUserAccount();
|
||||
void rebootSpeaker(); //Using this command controllers can reboot HEOS device. This command can only be used to reboot the HEOS device to which the controller is connected through CLI port.
|
||||
void prettifyJsonResponse(bool enable); //Helper command to prettify JSON response when user is running CLI controller through telnet.
|
||||
|
||||
//Player Get Calls
|
||||
void getPlayers(); //get a list of players associated with this heos master
|
||||
void getPlayerState(int playerId);
|
||||
void getVolume(int playerId);
|
||||
void getNowPlayingMedia(int playerId);
|
||||
void getMute(int playerId);
|
||||
void getPlayMode(int playerId);
|
||||
void getQueue(int playerId);
|
||||
|
||||
//Group Get Calls
|
||||
void getGroups();
|
||||
void getGroupInfo(int groupId);
|
||||
void getGroupVolume(int groupId);
|
||||
void getGroupMute(int groupId);
|
||||
|
||||
//Player Set Calls
|
||||
void setPlayerState(int playerId, PLAYER_STATE state);
|
||||
void setVolume(int playerId, int volume); //Player volume level 0 to 100
|
||||
void setMute(int playerId, bool mute);
|
||||
void setPlayMode(int playerId, REPEAT_MODE repeatMode, bool shuffle); //shuffle and repead mode
|
||||
void playNext(int playerId);
|
||||
void playPrevious(int playerId);
|
||||
void volumeUp(int playerId, int step = 5); //steps 0-10
|
||||
void volumeDown(int playerId, int step = 5); //steps 0-10
|
||||
void clearQueue(int playerId);
|
||||
void moveQueue(int playerId, int sourcQueueId, int destinationQueueId);
|
||||
void checkForFirmwareUpdate(int playerId);
|
||||
|
||||
//Group Set Calls
|
||||
void setGroupVolume(int groupId, bool volume);
|
||||
void setGroupMute(int groupId, bool mute);
|
||||
void toggleGroupMute(int groupId);
|
||||
void groupVolumeUp(int groupId, int step = 5);
|
||||
void groupVolumeDown(int groupId, int step = 5);
|
||||
|
||||
//Browse Get Commands
|
||||
void getMusicSources();
|
||||
void getSourceInfo(SOURCE_ID sourceId);
|
||||
void getSearchCriteria(SOURCE_ID sourceId);
|
||||
void browseSource(SOURCE_ID sourceId);
|
||||
//void search();
|
||||
|
||||
|
||||
private:
|
||||
bool m_eventRegistered = false;
|
||||
QHostAddress m_hostAddress;
|
||||
QTcpSocket *m_socket = nullptr;
|
||||
QHash<int, HeosPlayer*> m_heosPlayers;
|
||||
void setConnected(const bool &connected);
|
||||
|
||||
signals:
|
||||
void playerDiscovered(HeosPlayer *heosPlayer);
|
||||
void connectionStatusChanged(bool status);
|
||||
|
||||
void playStateReceived(int playerId, PLAYER_STATE state);
|
||||
void shuffleModeReceived(int playerId, bool shuffle);
|
||||
void repeatModeReceived(int playerId, REPEAT_MODE repeatMode);
|
||||
void muteStatusReceived(int playerId, bool mute);
|
||||
void volumeStatusReceived(int playerId, int volume);
|
||||
void nowPlayingMediaStatusReceived(int playerId, SOURCE_ID source, QString artist, QString album, QString Song, QString artwork);
|
||||
|
||||
private slots:
|
||||
void onConnected();
|
||||
void onDisconnected();
|
||||
void onError(QAbstractSocket::SocketError socketError);
|
||||
void readData();
|
||||
};
|
||||
|
||||
|
||||
#endif // HEOS_H
|
||||
95
denon/heosplayer.cpp
Normal file
95
denon/heosplayer.cpp
Normal file
@ -0,0 +1,95 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2019 Bernhard Trinnes <bernhard.trinnes@nymea.io> *
|
||||
* *
|
||||
* This file is part of nymea. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Lesser General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2.1 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; If not, see *
|
||||
* <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "heosplayer.h"
|
||||
|
||||
HeosPlayer::HeosPlayer(int playerId, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_playerId(playerId)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
HeosPlayer::HeosPlayer(int playerId, QString name, QString serialNumber, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_playerId(playerId),
|
||||
m_serialNumber(serialNumber),
|
||||
m_name(name)
|
||||
{
|
||||
}
|
||||
|
||||
QString HeosPlayer::name()
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
void HeosPlayer::setName(QString name)
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
|
||||
int HeosPlayer::playerId()
|
||||
{
|
||||
return m_playerId;
|
||||
}
|
||||
|
||||
int HeosPlayer::groupId()
|
||||
{
|
||||
return m_groupId;
|
||||
}
|
||||
|
||||
void HeosPlayer::setGroupId(int groupId)
|
||||
{
|
||||
m_groupId = groupId;
|
||||
}
|
||||
|
||||
QString HeosPlayer::playerModel()
|
||||
{
|
||||
return m_playerModel;
|
||||
}
|
||||
|
||||
QString HeosPlayer::playerVersion()
|
||||
{
|
||||
return m_playerVersion;
|
||||
}
|
||||
|
||||
QString HeosPlayer::network()
|
||||
{
|
||||
return m_network;
|
||||
}
|
||||
|
||||
QString HeosPlayer::serialNumber()
|
||||
{
|
||||
return m_serialNumber;
|
||||
}
|
||||
|
||||
QString HeosPlayer::lineOut()
|
||||
{
|
||||
return m_lineOut;
|
||||
}
|
||||
|
||||
QString HeosPlayer::control()
|
||||
{
|
||||
return m_control;
|
||||
}
|
||||
|
||||
|
||||
60
denon/heosplayer.h
Normal file
60
denon/heosplayer.h
Normal file
@ -0,0 +1,60 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2019 Bernhard Trinnes <bernhard.trinnes@nymea.io> *
|
||||
* *
|
||||
* This file is part of nymea. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Lesser General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2.1 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; If not, see *
|
||||
* <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef HEOSPLAYER_H
|
||||
#define HEOSPLAYER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class HeosPlayer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit HeosPlayer(int playerId, QObject *parent = nullptr);
|
||||
explicit HeosPlayer(int playerId, QString name, QString serialNumber, QObject *parent = nullptr);
|
||||
|
||||
QString name();
|
||||
void setName(QString name);
|
||||
int playerId();
|
||||
int groupId();
|
||||
void setGroupId(int groupId);
|
||||
QString playerModel();
|
||||
QString playerVersion();
|
||||
QString network();
|
||||
QString serialNumber();
|
||||
QString lineOut();
|
||||
QString control();
|
||||
|
||||
private:
|
||||
int m_playerId;
|
||||
int m_groupId;
|
||||
QString m_serialNumber;
|
||||
QString m_name;
|
||||
QString m_lineOut;
|
||||
QString m_network;
|
||||
QString m_playerModel;
|
||||
QString m_playerVersion;
|
||||
QString m_control;
|
||||
|
||||
};
|
||||
|
||||
#endif // HEOSPLAYER_H
|
||||
137
denon/heostypes.h
Normal file
137
denon/heostypes.h
Normal file
@ -0,0 +1,137 @@
|
||||
#ifndef HEOSTYPES_H
|
||||
#define HEOSTYPES_H
|
||||
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
enum NETWORK_TYPE {
|
||||
NETWORK_TYPE_WIRED,
|
||||
NETWORK_TYPE_WIFI
|
||||
} ;
|
||||
|
||||
enum LINEOUT_LEVEL_TYPE {
|
||||
LINEOUT_LEVEL_TYPE_VARIABLE = 1,
|
||||
LINEOUT_LEVEL_TYP_FIXED = 2
|
||||
};
|
||||
|
||||
enum CONTROL_TYPE {
|
||||
CONTROL_TYPE_NONE = 1,
|
||||
CONTROL_TYPE_IR = 2,
|
||||
CONTROL_TYPE_TRIGGER = 3,
|
||||
CONTROL_TYPE_NETWORK = 4
|
||||
};
|
||||
|
||||
enum PLAYER_STATE {
|
||||
PLAYER_STATE_PLAY,
|
||||
PLAYER_STATE_PAUSE,
|
||||
PLAYER_STATE_STOP
|
||||
};
|
||||
|
||||
enum NOW_PLAYING_OPTIONS {
|
||||
NOW_PLAYING_OPTION_THUMBS_UP = 11,
|
||||
NOW_PLAYING_OPTION_THUMBS_DOWN = 12,
|
||||
NOW_PLAYING_OPTION_ADD_STATION_TO_HEOS_FAVOURITES = 19
|
||||
};
|
||||
|
||||
enum REPEAT_MODE {
|
||||
REPEAT_MODE_OFF,
|
||||
REPEAT_MODE_ONE,
|
||||
REPEAT_MODE_ALL
|
||||
};
|
||||
|
||||
enum PLAYER_ROLE {
|
||||
PLAYER_ROLE_LEADER,
|
||||
PLAYER_ROLE_MEMBER
|
||||
};
|
||||
|
||||
enum BROWSE_OPTION {
|
||||
BROWSE_OPTION_ADD_TRACK_TO_LIBRARY = 1,
|
||||
BROWSE_OPTION_ADD_ALBUM_TO_LIBRARY = 2,
|
||||
BROWSE_OPTION_ADD_STATION_TO_LIBRARY = 3,
|
||||
BROWSE_OPTION_ADD_PLAYLIST_TO_LIBRARY = 4,
|
||||
BROWSE_OPTION_REMOVE_TRACK_FROM_LIBRARY = 5,
|
||||
BROWSE_OPTION_REMOVE_ALBUM_FROM_LIBRARY = 6,
|
||||
BROWSE_OPTION_REMOVE_STATION_FROM_LIBRARY = 7,
|
||||
BROWSE_OPTION_REMOVE_PLAYLIST_FROM_LIBRARY = 8,
|
||||
BROWSE_OPTION_CREATE_NEW_STATION = 13,
|
||||
BROWSE_OPTION_ADD_HEOS_FAVORITES = 19
|
||||
};
|
||||
|
||||
enum SEARCH_CRITERIA { // criteria id returned by 'get_search_criteria' command
|
||||
SEARCH_CRITERIA_ARTIST,
|
||||
SEARCH_CRITERIA_ALBUM,
|
||||
SEARCH_CRITERIA_SONG,
|
||||
SEARCH_CRITERIA_STATION
|
||||
};
|
||||
|
||||
enum SOURCE_ID {
|
||||
SOURCE_ID_PANDORA = 1,
|
||||
SOURCE_ID_RHAPSODY,
|
||||
SOURCE_ID_TUNEIN,
|
||||
SOURCE_ID_SPOTIFY,
|
||||
SOURCE_ID_DEEZER,
|
||||
SOURCE_ID_NAPSTER,
|
||||
SOURCE_ID_IHEARTRADIO,
|
||||
SOURCE_ID_SIRIUS_XM,
|
||||
SOURCE_ID_SOUNDCLOUD,
|
||||
SOURCE_ID_TIDAL,
|
||||
SOURCE_ID_FUTURE_SERVICE_1,
|
||||
SOURCE_ID_RDIO,
|
||||
SOURCE_ID_AMAZON_MUSIC,
|
||||
SOURCE_ID_FUTURE_SERVICE_2,
|
||||
SOURCE_ID_MOODMIX,
|
||||
SOURCE_ID_JUKE,
|
||||
SOURCE_ID_FUTURE_SERVICE_3,
|
||||
SOURCE_ID_QQMUSIC = 18,
|
||||
SOURCE_ID_LOCAL_MEDIA = 1024,
|
||||
SOURCE_ID_HEOS_PLAYLIST = 1025,
|
||||
SOURCE_ID_HEOS_HISTORY = 1026,
|
||||
SOURCE_ID_HEOS_AUX = 1027,
|
||||
SOURCE_ID_HEOS_FAVORITES = 1028
|
||||
};
|
||||
|
||||
struct SearchObject {
|
||||
int sourceId; //Source id returned by 'get_music_sources' command
|
||||
QString searchString; //String for search limited to 128 unicode characters and may contain '*' for wildcard if supported by search criteria id
|
||||
SEARCH_CRITERIA searchCriteria; //Search criteria id returned by 'get_search_criteria' command
|
||||
int count; //Total number of items available in the container. NOTE: count value of '0' indicates unknown container size. Controllers needs to query until the return payload is empty (returned attribute is 0).
|
||||
int range; //Range is start and end record index to return. Range parameter is optional. Omitting range parameter returns all records up to a maximum of 50/100 records per response. The default maximum number of records depend on the service type.
|
||||
int returned; //Number of items returned in current response
|
||||
};
|
||||
|
||||
struct MusicSourceObject {
|
||||
QString name;
|
||||
QString image_url;
|
||||
QString type;
|
||||
int sourceId;
|
||||
bool available;
|
||||
QString serviceUsername;
|
||||
};
|
||||
|
||||
struct PlayerObject {
|
||||
QString name;
|
||||
int playerId;
|
||||
PLAYER_ROLE role;
|
||||
};
|
||||
|
||||
struct GroupObject {
|
||||
QString name;
|
||||
int groupId;
|
||||
QList<PLAYER_ROLE> role;
|
||||
};
|
||||
|
||||
struct SourceContainersObject {
|
||||
int sourceId;
|
||||
int containerId;
|
||||
int range;
|
||||
int count;
|
||||
};
|
||||
|
||||
struct heosPlayer {
|
||||
|
||||
};
|
||||
|
||||
struct heosGroup {
|
||||
|
||||
};
|
||||
|
||||
#endif // HEOSTYPES_H
|
||||
Loading…
x
Reference in New Issue
Block a user