added bluos files
parent
49907ecb89
commit
6a199f5ae8
|
|
@ -0,0 +1,13 @@
|
|||
include(../plugins.pri)
|
||||
|
||||
QT += network
|
||||
|
||||
TARGET = $$qtLibraryTarget(nymea_integrationpluginbluos)
|
||||
|
||||
SOURCES += \
|
||||
integrationpluginbluos.cpp \
|
||||
bluos.cpp \
|
||||
|
||||
HEADERS += \
|
||||
integrationpluginbluos.h \
|
||||
bluos.h \
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2020, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project 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 project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
|
||||
#include "integrationpluginbluos.h"
|
||||
#include "plugininfo.h"
|
||||
#include "integrations/thing.h"
|
||||
#include "network/networkaccessmanager.h"
|
||||
#include "platform/platformzeroconfcontroller.h"
|
||||
#include "network/zeroconf/zeroconfservicebrowser.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QStringList>
|
||||
#include <QJsonDocument>
|
||||
#include <QTimer>
|
||||
|
||||
|
||||
IntegrationPluginBluOS::IntegrationPluginBluOS()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
IntegrationPluginBluOS::~IntegrationPluginBluOS()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void IntegrationPluginBluOS::init()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void IntegrationPluginBluOS::discoverThings(ThingDiscoveryInfo *info)
|
||||
{
|
||||
|
||||
if (info->thingClassId() == bluosThingClassId) {
|
||||
/*
|
||||
* 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:thing:ACT-Denon:1'.
|
||||
*/
|
||||
UpnpDiscoveryReply *reply = hardwareManager()->upnpDiscovery()->discoverDevices();
|
||||
connect(reply, &UpnpDiscoveryReply::finished, info, [this, reply, info](){
|
||||
reply->deleteLater();
|
||||
|
||||
if (reply->error() != UpnpDiscoveryReply::UpnpDiscoveryReplyErrorNoError) {
|
||||
qCWarning(dcDenon()) << "Upnp discovery error" << reply->error();
|
||||
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("UPnP discovery failed."));
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (const UpnpDeviceDescriptor &upnpDevice, reply->deviceDescriptors()) {
|
||||
qCDebug(dcDenon) << "UPnP thing found:" << upnpDevice.modelDescription() << upnpDevice.friendlyName() << upnpDevice.hostAddress().toString() << upnpDevice.modelName() << upnpDevice.manufacturer() << upnpDevice.serialNumber();
|
||||
|
||||
if (upnpDevice.modelName().contains("HEOS")) {
|
||||
QString serialNumber = upnpDevice.serialNumber();
|
||||
if (serialNumber != "0000001") {
|
||||
// child devices have serial number 0000001
|
||||
qCDebug(dcDenon) << "UPnP thing found:" << upnpDevice.modelDescription() << upnpDevice.friendlyName() << upnpDevice.hostAddress().toString() << upnpDevice.modelName() << upnpDevice.manufacturer() << upnpDevice.serialNumber();
|
||||
ThingDescriptor descriptor(heosThingClassId, upnpDevice.modelName(), serialNumber);
|
||||
ParamList params;
|
||||
foreach (Thing *existingThing, myThings()) {
|
||||
if (existingThing->paramValue(heosThingSerialNumberParamTypeId).toString().contains(serialNumber, Qt::CaseSensitivity::CaseInsensitive)) {
|
||||
descriptor.setThingId(existingThing->id());
|
||||
break;
|
||||
}
|
||||
}
|
||||
params.append(Param(heosThingModelNameParamTypeId, upnpDevice.modelName()));
|
||||
params.append(Param(heosThingIpParamTypeId, upnpDevice.hostAddress().toString()));
|
||||
params.append(Param(heosThingSerialNumberParamTypeId, serialNumber));
|
||||
descriptor.setParams(params);
|
||||
info->addThingDescriptor(descriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
return;
|
||||
}
|
||||
info->finish(Thing::ThingErrorThingClassNotFound);
|
||||
}
|
||||
|
||||
void IntegrationPluginDenon::setupThing(ThingSetupInfo *info)
|
||||
{
|
||||
Thing *thing = info->thing();
|
||||
|
||||
if (thing->thingClassId() == AVRX1000ThingClassId) {
|
||||
qCDebug(dcDenon) << "Setup Denon device" << thing->paramValue(AVRX1000ThingIpParamTypeId).toString();
|
||||
|
||||
QHostAddress address(thing->paramValue(AVRX1000ThingIpParamTypeId).toString());
|
||||
if (address.isNull()) {
|
||||
qCWarning(dcDenon) << "Could not parse ip address" << thing->paramValue(AVRX1000ThingIpParamTypeId).toString();
|
||||
info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("The given IP address is not valid."));
|
||||
return;
|
||||
}
|
||||
|
||||
AvrConnection *denonConnection = new AvrConnection(address, 23, this);
|
||||
connect(denonConnection, &AvrConnection::connectionStatusChanged, this, &IntegrationPluginDenon::onAvrConnectionChanged);
|
||||
connect(denonConnection, &AvrConnection::socketErrorOccured, this, &IntegrationPluginDenon::onAvrSocketError);
|
||||
connect(denonConnection, &AvrConnection::channelChanged, this, &IntegrationPluginDenon::onAvrChannelChanged);
|
||||
connect(denonConnection, &AvrConnection::powerChanged, this, &IntegrationPluginDenon::onAvrPowerChanged);
|
||||
connect(denonConnection, &AvrConnection::volumeChanged, this, &IntegrationPluginDenon::onAvrVolumeChanged);
|
||||
connect(denonConnection, &AvrConnection::surroundModeChanged, this, &IntegrationPluginDenon::onAvrSurroundModeChanged);
|
||||
connect(denonConnection, &AvrConnection::muteChanged, this, &IntegrationPluginDenon::onAvrMuteChanged);
|
||||
|
||||
m_avrConnections.insert(thing, denonConnection);
|
||||
m_asyncAvrSetups.insert(denonConnection, info);
|
||||
// In case the setup is cancelled before we finish it...
|
||||
connect(info, &QObject::destroyed, this, [this, info, denonConnection]() { m_asyncAvrSetups.remove(denonConnection); });
|
||||
|
||||
denonConnection->connectDevice();
|
||||
return;
|
||||
}
|
||||
|
||||
if (thing->thingClassId() == heosThingClassId) {
|
||||
qCDebug(dcDenon) << "Setup Denon device" << thing->paramValue(heosThingIpParamTypeId).toString();
|
||||
|
||||
QHostAddress address(thing->paramValue(heosThingIpParamTypeId).toString());
|
||||
Heos *heos = new Heos(address, this);
|
||||
connect(heos, &Heos::connectionStatusChanged, this, &IntegrationPluginDenon::onHeosConnectionChanged);
|
||||
connect(heos, &Heos::playerDiscovered, this, &IntegrationPluginDenon::onHeosPlayerDiscovered);
|
||||
connect(heos, &Heos::playStateReceived, this, &IntegrationPluginDenon::onHeosPlayStateReceived);
|
||||
connect(heos, &Heos::repeatModeReceived, this, &IntegrationPluginDenon::onHeosRepeatModeReceived);
|
||||
connect(heos, &Heos::shuffleModeReceived, this, &IntegrationPluginDenon::onHeosShuffleModeReceived);
|
||||
connect(heos, &Heos::muteStatusReceived, this, &IntegrationPluginDenon::onHeosMuteStatusReceived);
|
||||
connect(heos, &Heos::volumeStatusReceived, this, &IntegrationPluginDenon::onHeosVolumeStatusReceived);
|
||||
connect(heos, &Heos::nowPlayingMediaStatusReceived, this, &IntegrationPluginDenon::onHeosNowPlayingMediaStatusReceived);
|
||||
|
||||
m_heos.insert(thing, heos);
|
||||
m_asyncHeosSetups.insert(heos, info);
|
||||
// In case the setup is cancelled before we finish it...
|
||||
connect(info, &QObject::destroyed, this, [this, info, heos]() { m_asyncHeosSetups.remove(heos); });
|
||||
|
||||
heos->connectHeos();
|
||||
return;
|
||||
}
|
||||
|
||||
if (thing->thingClassId() == heosPlayerThingClassId) {
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
return;
|
||||
}
|
||||
info->finish(Thing::ThingErrorThingClassNotFound);
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2020, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project 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 project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef INTEGRATIONPLUGINBLUOS_H
|
||||
#define INTEGRATIONPLUGINBLUOS_H
|
||||
|
||||
#include "integrations/integrationplugin.h"
|
||||
|
||||
#include <QUdpSocket>
|
||||
|
||||
#include <QNetworkAccessManager>
|
||||
|
||||
class PluginTimer;
|
||||
|
||||
class IntegrationPluginBluOS: public IntegrationPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginbluos.json")
|
||||
Q_INTERFACES(IntegrationPlugin)
|
||||
|
||||
public:
|
||||
explicit IntegrationPluginBluOS();
|
||||
~IntegrationPluginBluOS();
|
||||
|
||||
void init() override;
|
||||
void discoverThings(ThingDiscoveryInfo *info) override;
|
||||
void setupThing(ThingSetupInfo *info) override;
|
||||
void thingRemoved(Thing *thing) override;
|
||||
void executeAction(ThingActionInfo *info) override;
|
||||
|
||||
private slots:
|
||||
void refreshStates();
|
||||
|
||||
private:
|
||||
|
||||
private:
|
||||
PluginTimer *m_pollTimer = nullptr;
|
||||
|
||||
};
|
||||
|
||||
#endif // INTEGRATIONPLUGINBLUOS_H
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
{
|
||||
"displayName": "BluOS",
|
||||
"name": "bluos",
|
||||
"id": "",
|
||||
"vendors": [
|
||||
{
|
||||
"id": "cf0a9644-2c13-4daf-85c1-ad88d6745b42",
|
||||
"displayName": "Denon",
|
||||
"name": "denon",
|
||||
"thingClasses": [
|
||||
{
|
||||
"id": "fce5247f-4c6d-408f-ac62-e5973dc6adfa",
|
||||
"name": "heosPlayer",
|
||||
"displayName": "Heos player",
|
||||
"createMethods": ["auto"],
|
||||
"interfaces": ["mediaplayer", "mediacontroller", "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": "866e8d6a-953f-4bdc-8d85-8d92e51e8592",
|
||||
"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": ""
|
||||
},
|
||||
{
|
||||
"id": "c59835ac-ee6e-4e6c-aa20-aeb3501937c5",
|
||||
"name": "playerType",
|
||||
"displayName": "Player type",
|
||||
"displayNameEvent": "Player type changed",
|
||||
"possibleValues": [
|
||||
"audio",
|
||||
"video"
|
||||
],
|
||||
"type": "QString",
|
||||
"defaultValue": "audio"
|
||||
}
|
||||
],
|
||||
"actionTypes": [
|
||||
{
|
||||
"id": "a718f7e9-0b54-4403-b661-49f7b0d13085",
|
||||
"name": "skipBack",
|
||||
"displayName": "Akip back"
|
||||
},
|
||||
{
|
||||
"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": "57697e9c-ce5e-4b8f-b42e-16662829ceb2",
|
||||
"name": "skipNext",
|
||||
"displayName": "Skip next"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_devicepluginbluos.so
|
||||
Loading…
Reference in New Issue