added denon heos
parent
3206f285ba
commit
ac3a209afe
|
|
@ -6,8 +6,12 @@ TARGET = $$qtLibraryTarget(nymea_deviceplugindenon)
|
|||
|
||||
SOURCES += \
|
||||
deviceplugindenon.cpp \
|
||||
denonconnection.cpp
|
||||
denonconnection.cpp \
|
||||
heos.cpp \
|
||||
heosplayer.cpp \
|
||||
|
||||
HEADERS += \
|
||||
deviceplugindenon.h \
|
||||
denonconnection.h
|
||||
denonconnection.h \
|
||||
heos.h \
|
||||
heosplayer.h \
|
||||
|
|
|
|||
|
|
@ -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,89 +21,166 @@
|
|||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
/*!
|
||||
\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 "network/upnp/upnpdiscovery.h"
|
||||
#include "network/upnp/upnpdiscoveryreply.h"
|
||||
#include "network/avahi/qtavahiservicebrowser.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QStringList>
|
||||
#include <QJsonDocument>
|
||||
|
||||
DevicePluginDenon::DevicePluginDenon()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
DevicePluginDenon::~DevicePluginDenon()
|
||||
DeviceManager::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) {
|
||||
QtAvahiServiceBrowser *avahiBrowser = hardwareManager()->avahiBrowser();
|
||||
connect(avahiBrowser, &QtAvahiServiceBrowser::serviceEntryAdded, this, &DevicePluginDenon::onAvahiEntryAdded);
|
||||
return DeviceManager::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 DeviceManager::DeviceErrorAsync;
|
||||
}
|
||||
return DeviceManager::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 DeviceManager::DeviceSetupStatusFailure;
|
||||
}
|
||||
|
||||
DenonConnection *denonConnection = new DenonConnection(address, 23, this);
|
||||
connect(denonConnection, &DenonConnection::connectionStatusChanged, this, &DevicePluginDenon::onAVRConnectionChanged);
|
||||
connect(denonConnection, &DenonConnection::socketErrorOccured, this, &DevicePluginDenon::onAVRSocketError);
|
||||
connect(denonConnection, &DenonConnection::dataReady, this, &DevicePluginDenon::onAVRDataReceived);
|
||||
|
||||
m_asyncSetups.append(denonConnection);
|
||||
denonConnection->connectDenon();
|
||||
m_denonConnections.insert(device, denonConnection);
|
||||
return DeviceManager::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);
|
||||
|
||||
return Device::DeviceSetupStatusAsync;
|
||||
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);
|
||||
|
||||
heos->connectHeos();
|
||||
m_heos.insert(device, heos);
|
||||
return DeviceManager::DeviceSetupStatusAsync;
|
||||
}
|
||||
|
||||
if (device->deviceClassId() == heosPlayerDeviceClassId) {
|
||||
return DeviceManager::DeviceSetupStatusSuccess;
|
||||
}
|
||||
|
||||
return DeviceManager::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) {
|
||||
DenonConnection *denonConnection = m_denonConnections.value(device);
|
||||
m_denonConnections.remove(device);
|
||||
denonConnection->disconnectDenon();
|
||||
denonConnection->deleteLater();
|
||||
}
|
||||
|
||||
if (device->deviceClassId() == heosDeviceClassId) {
|
||||
if (m_denonConnections.contains(device)) {
|
||||
DenonConnection *denonConnection = m_denonConnections.value(device);
|
||||
m_denonConnections.remove(device);
|
||||
denonConnection->disconnectDenon();
|
||||
denonConnection->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
if (device->deviceClassId() == heosPlayerDeviceClassId) {
|
||||
}
|
||||
|
||||
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) {
|
||||
DenonConnection *denonConnection = m_denonConnections.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);
|
||||
denonConnection->sendData(cmd);
|
||||
} else {
|
||||
QByteArray cmd = "PWSTANDBY\r";
|
||||
qCDebug(dcDenon) << "Execute power: " << action.id() << cmd;
|
||||
m_denonConnection->sendData(cmd);
|
||||
denonConnection->sendData(cmd);
|
||||
}
|
||||
|
||||
return Device::DeviceErrorNoError;
|
||||
|
|
@ -114,7 +191,7 @@ Device::DeviceError DevicePluginDenon::executeAction(Device *device, const Actio
|
|||
QByteArray cmd = "MV" + vol + "\r";
|
||||
|
||||
qCDebug(dcDenon) << "Execute volume" << action.id() << cmd;
|
||||
m_denonConnection->sendData(cmd);
|
||||
denonConnection->sendData(cmd);
|
||||
|
||||
return Device::DeviceErrorNoError;
|
||||
|
||||
|
|
@ -125,134 +202,454 @@ Device::DeviceError DevicePluginDenon::executeAction(Device *device, const Actio
|
|||
QByteArray cmd = "SI" + channel + "\r";
|
||||
|
||||
qCDebug(dcDenon) << "Change to channel:" << cmd;
|
||||
m_denonConnection->sendData(cmd);
|
||||
denonConnection->sendData(cmd);
|
||||
|
||||
return Device::DeviceErrorNoError;
|
||||
return DeviceManager::DeviceErrorNoError;
|
||||
} else if (action.actionTypeId() == AVRX1000IncreaseVolumeActionTypeId) {
|
||||
QByteArray cmd = "MVUP\r";
|
||||
qCDebug(dcDenon) << "Execute volume increase" << action.id() << cmd;
|
||||
denonConnection->sendData(cmd);
|
||||
|
||||
return DeviceManager::DeviceErrorNoError;
|
||||
} else if (action.actionTypeId() == AVRX1000DecreaseVolumeActionTypeId) {
|
||||
QByteArray cmd = "MVDOWN\r";
|
||||
qCDebug(dcDenon) << "Execute volume decrease" << action.id() << cmd;
|
||||
denonConnection->sendData(cmd);
|
||||
return DeviceManager::DeviceErrorNoError;
|
||||
}
|
||||
return Device::DeviceErrorActionTypeNotFound;
|
||||
|
||||
|
||||
return DeviceManager::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 DeviceManager::DeviceErrorNoError;
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == heosPlayerMuteActionTypeId) {
|
||||
bool mute = action.param(heosPlayerMuteActionMuteParamTypeId).value().toBool();
|
||||
heos->setMute(playerId, mute);
|
||||
return DeviceManager::DeviceErrorNoError;
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == heosPlayerPlaybackStatusActionTypeId) {
|
||||
QString playbackStatus = action.param(heosPlayerPlaybackStatusActionPlaybackStatusParamTypeId).value().toString();
|
||||
if (playbackStatus == "playing") {
|
||||
heos->setPlayerState(playerId, Heos::HeosPlayerState::Play);
|
||||
} else if (playbackStatus == "stopping") {
|
||||
heos->setPlayerState(playerId, Heos::HeosPlayerState::Stop);
|
||||
} else if (playbackStatus == "pausing") {
|
||||
heos->setPlayerState(playerId, Heos::HeosPlayerState::Pause);
|
||||
}
|
||||
|
||||
return DeviceManager::DeviceErrorNoError;
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == heosPlayerShuffleActionTypeId) {
|
||||
bool shuffle = action.param(heosPlayerShuffleActionShuffleParamTypeId).value().toBool();
|
||||
Heos::HeosRepeatMode repeatMode;
|
||||
repeatMode = Heos::HeosRepeatMode::Off;
|
||||
heos->setPlayMode(playerId, repeatMode, shuffle);
|
||||
|
||||
return DeviceManager::DeviceErrorNoError;
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == heosPlayerSkipBackActionTypeId) {
|
||||
heos->playPrevious(playerId);
|
||||
return DeviceManager::DeviceErrorNoError;
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == heosPlayerFastRewindActionTypeId) {
|
||||
|
||||
return DeviceManager::DeviceErrorActionTypeNotFound;
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == heosPlayerStopActionTypeId) {
|
||||
heos->setPlayerState(playerId, Heos::HeosPlayerState::Stop);
|
||||
return DeviceManager::DeviceErrorNoError;
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == heosPlayerPlayActionTypeId) {
|
||||
heos->setPlayerState(playerId, Heos::HeosPlayerState::Play);
|
||||
return DeviceManager::DeviceErrorNoError;
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == heosPlayerPauseActionTypeId) {
|
||||
|
||||
heos->setPlayerState(playerId, Heos::HeosPlayerState::Pause);
|
||||
return DeviceManager::DeviceErrorNoError;
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == heosPlayerFastForwardActionTypeId) {
|
||||
|
||||
return DeviceManager::DeviceErrorActionTypeNotFound;
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == heosPlayerSkipNextActionTypeId) {
|
||||
heos->playNext(playerId);
|
||||
return DeviceManager::DeviceErrorNoError;
|
||||
}
|
||||
|
||||
return DeviceManager::DeviceErrorActionTypeNotFound;
|
||||
}
|
||||
return Device::DeviceErrorDeviceClassNotFound;
|
||||
}
|
||||
|
||||
void DevicePluginDenon::postSetupDevice(Device *device)
|
||||
{
|
||||
if (device->deviceClassId() == AVRX1000DeviceClassId) {
|
||||
|
||||
}
|
||||
|
||||
if (device->deviceClassId() == heosDeviceClassId) {
|
||||
Heos *heos = m_heos.value(device);
|
||||
heos->getPlayers();
|
||||
device->setStateValue(heosConnectedStateTypeId, heos->connected());
|
||||
}
|
||||
|
||||
if (device->deviceClassId() == heosPlayerDeviceClassId) {
|
||||
|
||||
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);
|
||||
device->setStateValue(heosPlayerConnectedStateTypeId, heos->connected());
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginDenon::startMonitoringAutoDevices()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
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(DenonConnection *denonConnection, m_denonConnections.values()) {
|
||||
if (!denonConnection->connected()) {
|
||||
denonConnection->connectDenon();
|
||||
}
|
||||
Device *device = m_denonConnections.key(denonConnection);
|
||||
if (device->deviceClassId() == AVRX1000DeviceClassId) {
|
||||
denonConnection->sendData("PW?\rSI?\rMV?\r");
|
||||
}
|
||||
}
|
||||
|
||||
// Set connection status
|
||||
m_device->setStateValue(AVRX1000ConnectedStateTypeId, m_denonConnection->connected());
|
||||
foreach(Device *device, myDevices()) {
|
||||
|
||||
if (device->deviceClassId() == heosDeviceClassId) {
|
||||
Heos *heos = m_heos.value(device);
|
||||
if (!heos->connected()) {
|
||||
heos->connectHeos();
|
||||
}
|
||||
device->setStateValue(heosConnectedStateTypeId, heos->connected());
|
||||
heos->getPlayers();
|
||||
heos->registerForChangeEvents(true);
|
||||
}
|
||||
|
||||
|
||||
if (device->deviceClassId() == heosPlayerDeviceClassId) {
|
||||
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::onDataReceived(const QByteArray &data)
|
||||
void DevicePluginDenon::onAVRConnectionChanged()
|
||||
{
|
||||
DenonConnection *denonConnection = static_cast<DenonConnection *>(sender());
|
||||
Device *device = m_denonConnections.key(denonConnection);
|
||||
|
||||
if (device->deviceClassId() == AVRX1000DeviceClassId) {
|
||||
// if the device is connected
|
||||
if (denonConnection->connected()) {
|
||||
// and from the first setup
|
||||
if (m_asyncSetups.contains(denonConnection)) {
|
||||
m_asyncSetups.removeAll(denonConnection);
|
||||
denonConnection->sendData("PW?\rSI?\rMV?\r");
|
||||
emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusSuccess);
|
||||
}
|
||||
}
|
||||
device->setStateValue(AVRX1000ConnectedStateTypeId, denonConnection->connected());
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginDenon::onAVRDataReceived(const QByteArray &data)
|
||||
{
|
||||
DenonConnection *denonConnection = static_cast<DenonConnection *>(sender());
|
||||
Device *device = m_denonConnections.key(denonConnection);
|
||||
qCDebug(dcDenon) << "Data received" << data;
|
||||
|
||||
// if there is no device, return
|
||||
if (m_device.isNull())
|
||||
return;
|
||||
if (device->deviceClassId() == AVRX1000DeviceClassId) {
|
||||
if (data.contains("MV") && !data.contains("MAX")){
|
||||
int index = data.indexOf("MV");
|
||||
int vol = data.mid(index+2, 2).toInt();
|
||||
|
||||
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";
|
||||
qCDebug(dcDenon) << "Update volume:" << vol;
|
||||
device->setStateValue(AVRX1000VolumeStateTypeId, vol);
|
||||
}
|
||||
|
||||
qCDebug(dcDenon) << "Update channel:" << cmd;
|
||||
m_device->setStateValue(AVRX1000ChannelStateTypeId, cmd);
|
||||
}
|
||||
if (data.contains("SI")) {
|
||||
QString cmd = NULL;
|
||||
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 (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);
|
||||
qCDebug(dcDenon) << "Update channel:" << cmd;
|
||||
device->setStateValue(AVRX1000ChannelStateTypeId, cmd);
|
||||
}
|
||||
|
||||
if (data.contains("PWON")) {
|
||||
qCDebug(dcDenon) << "Update power on";
|
||||
device->setStateValue(AVRX1000PowerStateTypeId, true);
|
||||
} else if (data.contains("PWSTANDBY")) {
|
||||
qCDebug(dcDenon) << "Update power off";
|
||||
device->setStateValue(AVRX1000PowerStateTypeId, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginDenon::onSocketError()
|
||||
|
||||
void DevicePluginDenon::onAVRSocketError()
|
||||
{
|
||||
// if there is no device, return
|
||||
if (m_device.isNull())
|
||||
return;
|
||||
DenonConnection *denonConnection = static_cast<DenonConnection *>(sender());
|
||||
Device *device = m_denonConnections.key(denonConnection);
|
||||
if (device->deviceClassId() == AVRX1000DeviceClassId) {
|
||||
|
||||
// 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();
|
||||
// Check if setup running for this device
|
||||
if (m_asyncSetups.contains(denonConnection)) {
|
||||
m_asyncSetups.removeAll(denonConnection);
|
||||
qCWarning(dcDenon()) << "Could not add device. The setup failed.";
|
||||
emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusFailure);
|
||||
// Delete the connection, the device will not be added and
|
||||
// the connection will be created in the next setup
|
||||
denonConnection->deleteLater();
|
||||
m_denonConnections.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<QString> serialNumbers;
|
||||
foreach (Device *device, myDevices()) {
|
||||
if (device->deviceClassId() == heosDeviceClassId){
|
||||
serialNumbers.append(device->paramValue(heosDeviceSerialNumberParamTypeId).toString());
|
||||
}
|
||||
}
|
||||
|
||||
QList<DeviceDescriptor> deviceDescriptors;
|
||||
foreach (const UpnpDeviceDescriptor &upnpDevice, reply->deviceDescriptors()) {
|
||||
qCDebug(dcDenon) << "UPnP device found:" << upnpDevice.modelDescription() << upnpDevice.friendlyName() << upnpDevice.hostAddress().toString() << upnpDevice.modelName() << upnpDevice.manufacturer() << upnpDevice.serialNumber() << upnpDevice.deviceType() << upnpDevice.location();
|
||||
if (upnpDevice.modelName().contains("HEOS")) {
|
||||
//check if not already addded
|
||||
QString serialNumber = upnpDevice.serialNumber();
|
||||
if ((!serialNumbers.contains(serialNumber)) && (serialNumber !=("0000001"))) {
|
||||
DeviceDescriptor descriptor(heosDeviceClassId, upnpDevice.modelName(), serialNumber);
|
||||
ParamList params;
|
||||
params.append(Param(heosDeviceModelNameParamTypeId, upnpDevice.modelName()));
|
||||
params.append(Param(heosDeviceIpParamTypeId, upnpDevice.hostAddress().toString()));
|
||||
params.append(Param(heosDeviceSerialNumberParamTypeId, serialNumber));
|
||||
descriptor.setParams(params);
|
||||
deviceDescriptors.append(descriptor);
|
||||
serialNumbers.append(serialNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emit devicesDiscovered(heosDeviceClassId, deviceDescriptors);
|
||||
}
|
||||
|
||||
void DevicePluginDenon::onAvahiEntryAdded() {
|
||||
qCDebug(dcDenon) << "Avahi entry added";
|
||||
}
|
||||
|
||||
void DevicePluginDenon::onHeosConnectionChanged()
|
||||
{
|
||||
Heos *heos = static_cast<Heos *>(sender());
|
||||
heos->registerForChangeEvents(true);
|
||||
Device *device = m_heos.key(heos);
|
||||
if (!device->setupComplete() && heos->connected()) {
|
||||
emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
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, Heos::HeosPlayerState state)
|
||||
{
|
||||
foreach (Device *device, myDevices()) {
|
||||
if(device->deviceClassId() == heosPlayerDeviceClassId) {
|
||||
if (device->paramValue(heosPlayerDevicePlayerIdParamTypeId).toInt() == playerId) {
|
||||
if (state == Heos::HeosPlayerState::Pause) {
|
||||
device->setStateValue(heosPlayerPlaybackStatusStateTypeId, "Paused");
|
||||
} else if (state == Heos::HeosPlayerState::Play) {
|
||||
device->setStateValue(heosPlayerPlaybackStatusStateTypeId, "Playing");
|
||||
} else if (state == Heos::HeosPlayerState::Stop) {
|
||||
device->setStateValue(heosPlayerPlaybackStatusStateTypeId, "Stopped");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DevicePluginDenon::onHeosRepeatModeReceived(int playerId, Heos::HeosRepeatMode repeatMode)
|
||||
{
|
||||
foreach (Device *device, myDevices()) {
|
||||
if(device->deviceClassId() == heosPlayerDeviceClassId) {
|
||||
if (device->paramValue(heosPlayerDevicePlayerIdParamTypeId).toInt() == playerId) {
|
||||
if (repeatMode == Heos::HeosRepeatMode::All) {
|
||||
device->setStateValue(heosPlayerRepeatStateTypeId, "All");
|
||||
} else if (repeatMode == Heos::HeosRepeatMode::One) {
|
||||
device->setStateValue(heosPlayerRepeatStateTypeId, "One");
|
||||
} else if (repeatMode == Heos::HeosRepeatMode::Off) {
|
||||
device->setStateValue(heosPlayerRepeatStateTypeId, "None");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginDenon::onHeosShuffleModeReceived(int playerId, bool shuffle)
|
||||
{
|
||||
foreach (Device *device, myDevices()) {
|
||||
if(device->deviceClassId() == heosPlayerDeviceClassId) {
|
||||
if (device->paramValue(heosPlayerDevicePlayerIdParamTypeId).toInt() == playerId) {
|
||||
|
||||
if (shuffle) {
|
||||
device->setStateValue(heosPlayerMuteStateTypeId, true);
|
||||
} else {
|
||||
device->setStateValue(heosPlayerMuteStateTypeId, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginDenon::onHeosMuteStatusReceived(int playerId, bool mute)
|
||||
{
|
||||
foreach (Device *device, myDevices()) {
|
||||
if(device->deviceClassId() == heosPlayerDeviceClassId) {
|
||||
if (device->paramValue(heosPlayerDevicePlayerIdParamTypeId).toInt() == playerId) {
|
||||
device->setStateValue(heosPlayerMuteStateTypeId, mute);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginDenon::onHeosVolumeStatusReceived(int playerId, int volume)
|
||||
{
|
||||
foreach (Device *device, myDevices()) {
|
||||
if(device->deviceClassId() == heosPlayerDeviceClassId) {
|
||||
if (device->paramValue(heosPlayerDevicePlayerIdParamTypeId).toInt() == playerId) {
|
||||
device->setStateValue(heosPlayerVolumeStateTypeId, volume);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePluginDenon::onHeosNowPlayingMediaStatusReceived(int playerId, QString source, QString artist, QString album, QString song, QString artwork)
|
||||
{
|
||||
foreach (Device *device, myDevices()) {
|
||||
if(device->deviceClassId() == heosPlayerDeviceClassId) {
|
||||
if (device->paramValue(heosPlayerDevicePlayerIdParamTypeId).toInt() == playerId) {
|
||||
device->setStateValue(heosPlayerArtistStateTypeId, artist);
|
||||
device->setStateValue(heosPlayerTitleStateTypeId, song);
|
||||
device->setStateValue(heosPlayerArtworkStateTypeId, artwork);
|
||||
device->setStateValue(heosPlayerCollectionStateTypeId, album);
|
||||
device->setStateValue(heosPlayerSourceStateTypeId, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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. *
|
||||
* *
|
||||
|
|
@ -26,7 +26,6 @@
|
|||
|
||||
#include "devices/deviceplugin.h"
|
||||
|
||||
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
|
|
@ -35,6 +34,8 @@
|
|||
|
||||
#include "plugintimer.h"
|
||||
#include "denonconnection.h"
|
||||
#include "heos.h"
|
||||
#include <QPair>
|
||||
|
||||
class DevicePluginDenon : public DevicePlugin
|
||||
{
|
||||
|
|
@ -45,27 +46,43 @@ class DevicePluginDenon : public DevicePlugin
|
|||
|
||||
public:
|
||||
explicit DevicePluginDenon();
|
||||
~DevicePluginDenon();
|
||||
|
||||
void init() override;
|
||||
Device::DeviceSetupStatus setupDevice(Device *device) override;
|
||||
void startMonitoringAutoDevices() override;
|
||||
DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override;
|
||||
DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
|
||||
void postSetupDevice(Device * device) override;
|
||||
DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
|
||||
void deviceRemoved(Device *device) override;
|
||||
Device::DeviceError executeAction(Device *device, const Action &action) override;
|
||||
|
||||
private:
|
||||
PluginTimer *m_pluginTimer = nullptr;
|
||||
QPointer<Device> m_device;
|
||||
QPointer<DenonConnection> m_denonConnection;
|
||||
QHash<Device *, DenonConnection*> m_denonConnections;
|
||||
QHash<Device *, Heos*> m_heos;
|
||||
|
||||
QList<DenonConnection *> m_asyncSetups;
|
||||
|
||||
QHash <ActionId, Device *> m_asyncActions;
|
||||
QHash <QNetworkReply *, ActionId> m_asyncActionReplies;
|
||||
QHash<int, Device *> m_playerIds;
|
||||
QHash<int, Device *> m_discoveredPlayerIds;
|
||||
QHash<const Action *, int> m_asyncActions;
|
||||
|
||||
|
||||
private slots:
|
||||
void onPluginTimer();
|
||||
void onConnectionChanged();
|
||||
void onDataReceived(const QByteArray &data);
|
||||
void onSocketError();
|
||||
void onAVRConnectionChanged();
|
||||
void onAVRDataReceived(const QByteArray &data);
|
||||
void onAVRSocketError();
|
||||
|
||||
void onUpnpDiscoveryFinished();
|
||||
void onAvahiEntryAdded();
|
||||
|
||||
void onHeosConnectionChanged();
|
||||
void onHeosPlayerDiscovered(HeosPlayer *heosPlayer);
|
||||
void onHeosPlayStateReceived(int playerId, Heos::HeosPlayerState state);
|
||||
void onHeosShuffleModeReceived(int playerId, bool shuffle);
|
||||
void onHeosRepeatModeReceived(int playerId, Heos::HeosRepeatMode repeatMode);
|
||||
void onHeosMuteStatusReceived(int playerId, bool mute);
|
||||
void onHeosVolumeStatusReceived(int playerId, int volume);
|
||||
void onHeosNowPlayingMediaStatusReceived(int playerId, QString source, QString artist, QString album, QString Song, QString artwork);
|
||||
};
|
||||
|
||||
#endif // DEVICEPLUGINDENON_H
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@
|
|||
"name": "AVRX1000",
|
||||
"displayName": "AVR X1000",
|
||||
"createMethods": ["user"],
|
||||
"deviceIcon": "Hifi",
|
||||
"interfaces": ["connectable", "power", "extendedvolumecontroller"],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "a54b98b4-b78f-41dd-a257-14425c6cf9ab",
|
||||
|
|
@ -42,16 +44,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
|
||||
},
|
||||
{
|
||||
|
|
@ -85,6 +96,251 @@
|
|||
],
|
||||
"defaultValue": "TUNER"
|
||||
}
|
||||
],
|
||||
"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": ["connectable", "extendedmediacontroller", "extendedvolumecontroller", "mediametadataprovider", "shufflerepeat" ],
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,475 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* 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>
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
|
||||
void Heos::getPlayers()
|
||||
{
|
||||
QByteArray cmd = "heos://player/get_players\r\n";
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
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::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, HeosPlayerState state)
|
||||
{
|
||||
QByteArray playerStateQuery;
|
||||
|
||||
if (state == HeosPlayerState::Play){
|
||||
playerStateQuery = "&state=play";
|
||||
} else if (state == HeosPlayerState::Pause){
|
||||
playerStateQuery = "&state=pause";
|
||||
} else if (state == HeosPlayerState::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, HeosRepeatMode repeatMode, bool shuffle)
|
||||
{
|
||||
QByteArray repeatModeQuery;
|
||||
|
||||
if (repeatMode == HeosRepeatMode::Off) {
|
||||
repeatModeQuery = "&repeat=off";
|
||||
} else if (repeatMode == HeosRepeatMode::One) {
|
||||
repeatModeQuery = "&repeat=on_one";
|
||||
} else if (repeatMode == HeosRepeatMode::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::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::getNowPlayingMedia(int playerId)
|
||||
{
|
||||
QByteArray cmd = "heos://player/get_now_playing_media?pid=" + QVariant(playerId).toByteArray() + "\r\n";
|
||||
m_socket->write(cmd);
|
||||
}
|
||||
|
||||
Heos::~Heos()
|
||||
{
|
||||
m_socket->close();
|
||||
}
|
||||
|
||||
bool Heos::connected()
|
||||
{
|
||||
return m_connected;
|
||||
}
|
||||
|
||||
void Heos::connectHeos()
|
||||
{
|
||||
if (m_socket->state() == QAbstractSocket::ConnectingState) {
|
||||
return;
|
||||
}
|
||||
m_socket->connectToHost(m_hostAddress, 1255);
|
||||
}
|
||||
|
||||
HeosPlayer *Heos::getPlayer(int playerId)
|
||||
{
|
||||
return m_heosPlayers.value(playerId);
|
||||
}
|
||||
|
||||
void Heos::onConnected()
|
||||
{
|
||||
qCDebug(dcDenon()) << "connected successfully to" << m_hostAddress.toString();
|
||||
setConnected(true);
|
||||
}
|
||||
|
||||
void Heos::onDisconnected()
|
||||
{
|
||||
qCDebug(dcDenon()) << "disconnected from" << m_hostAddress.toString();
|
||||
setConnected(false);
|
||||
}
|
||||
|
||||
void Heos::onError(QAbstractSocket::SocketError socketError)
|
||||
{
|
||||
qCWarning(dcDenon) << "socket error:" << socketError << m_socket->errorString();
|
||||
}
|
||||
|
||||
void Heos::readData()
|
||||
{
|
||||
int playerId;
|
||||
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();
|
||||
QString source;
|
||||
switch (dataMap.value("payload").toMap().value("sid").toInt()) {
|
||||
case 1:
|
||||
source = "Pandora";
|
||||
break;
|
||||
case 2:
|
||||
source = "Rhapsody";
|
||||
break;
|
||||
case 3:
|
||||
source = "TuneIn";
|
||||
break;
|
||||
case 4:
|
||||
source = "Spotify";
|
||||
break;
|
||||
case 5:
|
||||
source = "Deezer";
|
||||
break;
|
||||
case 6:
|
||||
source = "Napster";
|
||||
break;
|
||||
case 7:
|
||||
source = "iHeartRadio";
|
||||
break;
|
||||
case 8:
|
||||
source = "Sirius XM";
|
||||
break;
|
||||
case 9:
|
||||
source = "Soundcloud";
|
||||
break;
|
||||
case 10:
|
||||
source = "Tidal";
|
||||
break;
|
||||
case 11:
|
||||
source = "Unknown";
|
||||
break;
|
||||
case 12:
|
||||
source = "Rdio";
|
||||
break;
|
||||
case 13:
|
||||
source = "Amazon Music";
|
||||
break;
|
||||
case 14:
|
||||
source = "Unknown";
|
||||
break;
|
||||
case 15:
|
||||
source = "Moodmix";
|
||||
break;
|
||||
case 16:
|
||||
source = "Juke";
|
||||
break;
|
||||
case 17:
|
||||
source = "Unkown";
|
||||
break;
|
||||
case 18:
|
||||
source = "QQMusic";
|
||||
break;
|
||||
case 1024:
|
||||
source = "USB Media/DLNA Servers";
|
||||
break;
|
||||
case 1025:
|
||||
source = "HEOS Playlists";
|
||||
break;
|
||||
case 1026:
|
||||
source = "HEOS History";
|
||||
break;
|
||||
case 1027:
|
||||
source = "HEOS aux inputs";
|
||||
break;
|
||||
case 1028:
|
||||
source = "HEOS aux inputs";
|
||||
break;
|
||||
default:
|
||||
source = "Unknown";
|
||||
};
|
||||
emit nowPlayingMediaStatusReceived(playerId, source, artist, album, song, artwork);
|
||||
}
|
||||
|
||||
if (command.contains("get_play_state") || command.contains("set_play_state")) {
|
||||
if (message.hasQueryItem("state")) {
|
||||
HeosPlayerState playState = HeosPlayerState::Stop;
|
||||
if (message.queryItemValue("state").contains("play")) {
|
||||
playState = HeosPlayerState::Play;
|
||||
} else if (message.queryItemValue("state").contains("pause")) {
|
||||
playState = HeosPlayerState::Pause;
|
||||
} else if (message.queryItemValue("state").contains("stop")) {
|
||||
playState = HeosPlayerState::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);
|
||||
|
||||
HeosRepeatMode repeatMode = HeosRepeatMode::Off;
|
||||
if (message.queryItemValue("repeat").contains("on_all")){
|
||||
repeatMode = HeosRepeatMode::All;
|
||||
} else if (message.queryItemValue("repeat").contains("on_one")){
|
||||
repeatMode = HeosRepeatMode::One;
|
||||
} else if (message.queryItemValue("repeat").contains("off")){
|
||||
repeatMode = HeosRepeatMode::Off;
|
||||
}
|
||||
emit repeatModeReceived(playerId, repeatMode);
|
||||
}
|
||||
}
|
||||
|
||||
if (command.contains("player_state_changed")) {
|
||||
if (message.hasQueryItem("state")) {
|
||||
HeosPlayerState playState = HeosPlayerState::Stop;
|
||||
if (message.queryItemValue("state").contains("play")) {
|
||||
playState = HeosPlayerState::Play;
|
||||
} else if (message.queryItemValue("state").contains("pause")) {
|
||||
playState = HeosPlayerState::Pause;
|
||||
} else if (message.queryItemValue("state").contains("stop")) {
|
||||
playState = HeosPlayerState::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")) {
|
||||
HeosRepeatMode repeatMode = HeosRepeatMode::Off;
|
||||
if (message.queryItemValue("repeat").contains("on_all")){
|
||||
repeatMode = HeosRepeatMode::All;
|
||||
} else if (message.queryItemValue("repeat").contains("on_one")){
|
||||
repeatMode = HeosRepeatMode::One;
|
||||
} else if (message.queryItemValue("repeat").contains("off")){
|
||||
repeatMode = HeosRepeatMode::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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Heos::setConnected(const bool &connected)
|
||||
{
|
||||
m_connected = connected;
|
||||
emit connectionStatusChanged();
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
#ifndef HEOS_H
|
||||
#define HEOS_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QHostAddress>
|
||||
#include <QTcpSocket>
|
||||
#include "heosplayer.h"
|
||||
|
||||
class Heos : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum HeosPlayerState {
|
||||
Play = 0,
|
||||
Pause = 1,
|
||||
Stop = 2
|
||||
};
|
||||
|
||||
enum HeosRepeatMode {
|
||||
Off = 0,
|
||||
One = 1,
|
||||
All = 2
|
||||
};
|
||||
|
||||
explicit Heos(const QHostAddress &hostAddress, QObject *parent = 0);
|
||||
~Heos();
|
||||
|
||||
void setAddress(QHostAddress address);
|
||||
QHostAddress getAddress();
|
||||
bool connected();
|
||||
void connectHeos();
|
||||
|
||||
void getPlayers();
|
||||
HeosPlayer *getPlayer(int playerId);
|
||||
void getPlayerState(int playerId);
|
||||
void setPlayerState(int playerId, HeosPlayerState state);
|
||||
void getVolume(int playerId);
|
||||
void setVolume(int playerId, int volume);
|
||||
void getMute(int playerId);
|
||||
void setMute(int playerId, bool state);
|
||||
void setPlayMode(int playerId, HeosRepeatMode repeatMode, bool shuffle); //shuffle and repead mode
|
||||
void getPlayMode(int playerId);
|
||||
void playNext(int playerId);
|
||||
void playPrevious(int playerId);
|
||||
void getNowPlayingMedia(int playerId);
|
||||
void registerForChangeEvents(bool state);
|
||||
void sendHeartbeat();
|
||||
|
||||
private:
|
||||
bool m_connected;
|
||||
bool m_eventRegistered;
|
||||
QHostAddress m_hostAddress;
|
||||
QTcpSocket *m_socket;
|
||||
QHash<int, HeosPlayer*> m_heosPlayers;
|
||||
void setConnected(const bool &connected);
|
||||
|
||||
signals:
|
||||
void playerDiscovered(HeosPlayer *heosPlayer);
|
||||
void connectionStatusChanged();
|
||||
|
||||
void playStateReceived(int playerId, HeosPlayerState state);
|
||||
void shuffleModeReceived(int playerId, bool shuffle);
|
||||
void repeatModeReceived(int playerId, HeosRepeatMode repeatMode);
|
||||
void muteStatusReceived(int playerId, bool mute);
|
||||
void volumeStatusReceived(int playerId, int volume);
|
||||
void nowPlayingMediaStatusReceived(int playerId, QString 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
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
#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;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
#ifndef HEOSPLAYER_H
|
||||
#define HEOSPLAYER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class HeosPlayer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit HeosPlayer(int playerId, QObject *parent = 0);
|
||||
explicit HeosPlayer(int playerId, QString name, QString serialNumber, QObject *parent = 0);
|
||||
|
||||
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;
|
||||
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
};
|
||||
|
||||
#endif // HEOSPLAYER_H
|
||||
Loading…
Reference in New Issue