From 5498905a499b98adf7890a1cc4cc1c21a3971356 Mon Sep 17 00:00:00 2001 From: nymea Date: Thu, 11 Jul 2019 12:54:31 +0200 Subject: [PATCH] added missing files --- bose/bose.pro | 16 +++ bose/devicepluginbose.cpp | 277 +++++++++++++++++++++++++++++++++++++ bose/devicepluginbose.h | 67 +++++++++ bose/devicepluginbose.json | 187 +++++++++++++++++++++++++ 4 files changed, 547 insertions(+) create mode 100644 bose/bose.pro create mode 100644 bose/devicepluginbose.cpp create mode 100644 bose/devicepluginbose.h create mode 100644 bose/devicepluginbose.json diff --git a/bose/bose.pro b/bose/bose.pro new file mode 100644 index 00000000..a168666e --- /dev/null +++ b/bose/bose.pro @@ -0,0 +1,16 @@ +include(../plugins.pri) + +QT += \ + network \ + websockets \ + +TARGET = $$qtLibraryTarget(nymea_devicepluginbose) + +SOURCES += \ + devicepluginbose.cpp \ + soundtouch.cpp + +HEADERS += \ + devicepluginbose.h \ + soundtouch.h \ + soundtouchtypes.h diff --git a/bose/devicepluginbose.cpp b/bose/devicepluginbose.cpp new file mode 100644 index 00000000..e484e31c --- /dev/null +++ b/bose/devicepluginbose.cpp @@ -0,0 +1,277 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2019 Bernhard Trinnes . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "devicepluginbose.h" +#include "devices/device.h" +#include "plugininfo.h" +#include "platform/platformzeroconfcontroller.h" +#include "network/zeroconf/zeroconfservicebrowser.h" +#include "network/zeroconf/zeroconfserviceentry.h" + +#include +#include + +DevicePluginBose::DevicePluginBose() +{ +} + +DevicePluginBose::~DevicePluginBose() +{ +} + +void DevicePluginBose::init() +{ +} + +Device::DeviceSetupStatus DevicePluginBose::setupDevice(Device *device) +{ + if (!m_pluginTimer) { + m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(2); + connect(m_pluginTimer, &PluginTimer::timeout, this, &DevicePluginBose::onPluginTimer); + } + + if (device->deviceClassId() == soundtouchDeviceClassId) { + + connect(device, &Device::nameChanged, this, &DevicePluginBose::onDeviceNameChanged); + + QString ipAddress = device->paramValue(soundtouchDeviceIpParamTypeId).toString(); + SoundTouch *soundTouch = new SoundTouch(hardwareManager()->networkManager(), ipAddress, this); + connect(soundTouch, &SoundTouch::connectionChanged, this, &DevicePluginBose::onConnectionChanged); + + connect(soundTouch, &SoundTouch::infoReceived, this, &DevicePluginBose::onInfoObjectReceived); + connect(soundTouch, &SoundTouch::nowPlayingReceived, this, &DevicePluginBose::onNowPlayingObjectReceived); + connect(soundTouch, &SoundTouch::volumeReceived, this, &DevicePluginBose::onVolumeObjectReceived); + connect(soundTouch, &SoundTouch::sourcesReceived, this, &DevicePluginBose::onSourcesObjectReceived); + + soundTouch->getInfo(); + soundTouch->getNowPlaying(); + soundTouch->getVolume(); + soundTouch->getSources(); + + m_soundTouch.insert(device, soundTouch); + + return Device::DeviceSetupStatusSuccess; + } + return Device::DeviceSetupStatusFailure; +} + +void DevicePluginBose::deviceRemoved(Device *device) +{ + if (device->deviceClassId() == soundtouchDeviceClassId) { + SoundTouch *soundTouch = m_soundTouch.value(device); + soundTouch->deleteLater(); + } + + if (m_pluginTimer && myDevices().isEmpty()) { + hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer); + } +} + +Device::DeviceError DevicePluginBose::discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) +{ + Q_UNUSED(params) + Q_UNUSED(deviceClassId) + + ZeroConfServiceBrowser *serviceBrowser = hardwareManager()->zeroConfController()->createServiceBrowser("_soundtouch._tcp"); + QTimer::singleShot(5000, this, [this, serviceBrowser](){ + QList descriptors; + foreach (const ZeroConfServiceEntry avahiEntry, serviceBrowser->serviceEntries()) { + qCDebug(dcBose) << "Zeroconf entry:" << avahiEntry; + + QString playerId = avahiEntry.hostName().split(".").first(); + DeviceDescriptor descriptor(soundtouchDeviceClassId, avahiEntry.name(), avahiEntry.hostAddress().toString()); + ParamList params; + params << Param(soundtouchDeviceIpParamTypeId, avahiEntry.hostAddress().toString()); + params << Param(soundtouchDevicePlayerIdParamTypeId, playerId); + descriptor.setParams(params); + descriptors << descriptor; + } + emit devicesDiscovered(soundtouchDeviceClassId, descriptors); + serviceBrowser->deleteLater(); + }); + + return Device::DeviceErrorAsync; +} + +Device::DeviceError DevicePluginBose::executeAction(Device *device, const Action &action) +{ + if (device->deviceClassId() == soundtouchDeviceClassId) { + SoundTouch *soundTouch = m_soundTouch.value(device); + + if (action.actionTypeId() == soundtouchPowerActionTypeId) { + //bool power = action.param(soundtouchPowerActionPowerParamTypeId).value().toBool(); + soundTouch->setKey(KEY_VALUE::KEY_VALUE_POWER); //only toggling possible + return Device::DeviceErrorNoError; + } + if (action.actionTypeId() == soundtouchMuteActionTypeId) { + soundTouch->setKey(KEY_VALUE::KEY_VALUE_MUTE); + return Device::DeviceErrorNoError; + } + if (action.actionTypeId() == soundtouchPlayActionTypeId) { + soundTouch->setKey(KEY_VALUE::KEY_VALUE_PLAY); + return Device::DeviceErrorNoError; + } + if (action.actionTypeId() == soundtouchPauseActionTypeId) { + soundTouch->setKey(KEY_VALUE::KEY_VALUE_PAUSE); + return Device::DeviceErrorNoError; + } + if (action.actionTypeId() == soundtouchStopActionTypeId) { + soundTouch->setKey(KEY_VALUE::KEY_VALUE_STOP); + return Device::DeviceErrorNoError; + } + if (action.actionTypeId() == soundtouchSkipNextActionTypeId) { + soundTouch->setKey(KEY_VALUE::KEY_VALUE_NEXT_TRACK); + return Device::DeviceErrorNoError; + } + if (action.actionTypeId() == soundtouchSkipBackActionTypeId) { + soundTouch->setKey(KEY_VALUE::KEY_VALUE_PREV_TRACK); + return Device::DeviceErrorNoError; + } + + if (action.actionTypeId() == soundtouchShuffleActionTypeId) { + + bool shuffle = action.param(soundtouchShuffleActionShuffleParamTypeId).value().toBool(); + if (shuffle) { + soundTouch->setKey(KEY_VALUE::KEY_VALUE_SHUFFLE_ON); + } else { + soundTouch->setKey(KEY_VALUE::KEY_VALUE_SHUFFLE_OFF); + } + return Device::DeviceErrorNoError; + } + + if (action.actionTypeId() == soundtouchRepeatActionTypeId) { + + QString repeat = action.param(soundtouchRepeatActionRepeatParamTypeId).value().toString(); + if (repeat == "None") { + soundTouch->setKey(KEY_VALUE::KEY_VALUE_REPEAT_OFF); + } else if (repeat == "One") { + soundTouch->setKey(KEY_VALUE::KEY_VALUE_REPEAT_ONE); + } else if (repeat == "All") { + soundTouch->setKey(KEY_VALUE::KEY_VALUE_REPEAT_ALL); + } + return Device::DeviceErrorNoError; + } + + if (action.actionTypeId() == soundtouchVolumeActionTypeId) { + int volume = action.param(soundtouchVolumeActionVolumeParamTypeId).value().toInt(); + soundTouch->setVolume(volume); + return Device::DeviceErrorNoError; + } + + if (action.actionTypeId() == soundtouchPlaybackStatusActionTypeId) { + QString status = action.param(soundtouchPlaybackStatusActionPlaybackStatusParamTypeId).value().toString(); + if (status == "Playing") { + soundTouch->setKey(KEY_VALUE::KEY_VALUE_PLAY); + } else if (status == "Paused") { + soundTouch->setKey(KEY_VALUE::KEY_VALUE_PAUSE); + } else if (status == "Stopped") { + soundTouch->setKey(KEY_VALUE::KEY_VALUE_STOP); + } + return Device::DeviceErrorNoError; + } + + return Device::DeviceErrorActionTypeNotFound; + } + return Device::DeviceErrorDeviceClassNotFound; +} + +void DevicePluginBose::onPluginTimer() +{ + foreach(SoundTouch *soundTouch, m_soundTouch.values()) { + soundTouch->getInfo(); + soundTouch->getNowPlaying(); + soundTouch->getVolume(); + } +} + +void DevicePluginBose::onConnectionChanged(bool connected) +{ + SoundTouch *soundtouch = static_cast(sender()); + Device *device = m_soundTouch.key(soundtouch); + device->setStateValue(soundtouchConnectedStateTypeId, connected); +} + +void DevicePluginBose::onDeviceNameChanged() +{ + Device *device = static_cast(sender()); + SoundTouch *soundtouch = m_soundTouch.value(device); + soundtouch->setName(device->name()); +} + +void DevicePluginBose::onInfoObjectReceived(InfoObject infoObject) +{ + SoundTouch *soundtouch = static_cast(sender()); + Device *device = m_soundTouch.key(soundtouch); + device->setName(infoObject.name); +} + +void DevicePluginBose::onNowPlayingObjectReceived(NowPlayingObject nowPlaying) +{ + SoundTouch *soundtouch = static_cast(sender()); + Device *device = m_soundTouch.key(soundtouch); + + device->setStateValue(soundtouchTitleStateTypeId, nowPlaying.track); + device->setStateValue(soundtouchArtistStateTypeId, nowPlaying.artist); + device->setStateValue(soundtouchCollectionStateTypeId, nowPlaying.album); + device->setStateValue(soundtouchArtworkStateTypeId, nowPlaying.art.url); + device->setStateValue(soundtouchShuffleStateTypeId, ( nowPlaying.shuffleSetting == SHUFFLE_STATUS_SHUFFLE_ON )); + + switch (nowPlaying.repeatSettings) { + case REPEAT_STATUS_REPEAT_ONE: + device->setStateValue(soundtouchRepeatStateTypeId, "One"); + break; + case REPEAT_STATUS_REPEAT_ALL: + device->setStateValue(soundtouchRepeatStateTypeId, "All"); + break; + case REPEAT_STATUS_REPEAT_OFF: + device->setStateValue(soundtouchRepeatStateTypeId, "None"); + break; + } + + switch (nowPlaying.playStatus) { + case PLAY_STATUS_PLAY_STATE: + device->setStateValue(soundtouchPlaybackStatusStateTypeId, "Playing"); + break; + case PLAY_STATUS_PAUSE_STATE: + case PLAY_STATUS_BUFFERING_STATE: + device->setStateValue(soundtouchPlaybackStatusStateTypeId, "Paused"); + break; + case PLAY_STATUS_STOP_STATE: + device->setStateValue(soundtouchPlaybackStatusStateTypeId, "Stopped"); + break; + } +} + +void DevicePluginBose::onVolumeObjectReceived(VolumeObject volume) +{ + SoundTouch *soundtouch = static_cast(sender()); + Device *device = m_soundTouch.key(soundtouch); + device->setStateValue(soundtouchVolumeStateTypeId, volume.actualVolume); + device->setStateValue(soundtouchMuteStateTypeId, volume.muteEnabled); +} + +void DevicePluginBose::onSourcesObjectReceived(SourcesObject sources) +{ + foreach (SourceItemObject sourceItem, sources.sourceItems) { + qDebug(dcBose()) << "Source:" << sources.deviceId << sourceItem.source << sourceItem.displayName; + } +} diff --git a/bose/devicepluginbose.h b/bose/devicepluginbose.h new file mode 100644 index 00000000..382033a6 --- /dev/null +++ b/bose/devicepluginbose.h @@ -0,0 +1,67 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2019 Bernhard Trinnes . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef DEVICEPLUGINBOSE_H +#define DEVICEPLUGINBOSE_H + +#include "devices/deviceplugin.h" +#include "plugintimer.h" +#include "soundtouch.h" +#include "soundtouchtypes.h" + +#include +#include + +class DevicePluginBose : public DevicePlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "devicepluginbose.json") + Q_INTERFACES(DevicePlugin) + +public: + explicit DevicePluginBose(); + ~DevicePluginBose() override; + + void init() override; + Device::DeviceSetupStatus setupDevice(Device *device) override; + void deviceRemoved(Device *device) override; + Device::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override; + Device::DeviceError executeAction(Device *device, const Action &action) override; + +private: + PluginTimer *m_pluginTimer = nullptr; + + QHash m_soundTouch; + QHash m_pendingActions; + +private slots: + void onPluginTimer(); + void onConnectionChanged(bool connected); + void onDeviceNameChanged(); + + void onInfoObjectReceived(InfoObject infoObject); + void onNowPlayingObjectReceived(NowPlayingObject nowPlaying); + void onVolumeObjectReceived(VolumeObject volume); + void onSourcesObjectReceived(SourcesObject sources); +}; + +#endif // DEVICEPLUGINBOSE_H diff --git a/bose/devicepluginbose.json b/bose/devicepluginbose.json new file mode 100644 index 00000000..9d6a0575 --- /dev/null +++ b/bose/devicepluginbose.json @@ -0,0 +1,187 @@ +{ + "id": "472a3f24-b05c-49b3-ad9a-dfda608b6760", + "name": "Bose", + "displayName": "Bose", + "vendors": [ + { + "id": "433c45cd-5bc1-4239-a8a1-487c70ffdfc7", + "name": "bose", + "displayName": "Bose", + "deviceClasses": [ + { + "id": "f9b7a3f5-6353-48b1-afc1-66f914412f82", + "name": "soundtouch", + "displayName": "SoundTouch", + "interfaces": ["extendedvolumecontroller", "mediametadataprovider", "shufflerepeat", "connectable"], + "createMethods": ["discovery"], + "paramTypes": [ + { + "id": "1a897065-57c6-49b3-bac9-1e5db27859e5", + "name": "ip", + "displayName": "IP", + "type" : "QString", + "inputType": "IPv4Address" + }, + { + "id": "3eb95eef-e8ba-4d44-8a21-7d8038b74c4d", + "name": "playerId", + "displayName": "Player ID", + "type" : "QString" + } + ], + "stateTypes": [ + { + "id": "09dfbd40-c97c-4a20-9ecd-f80e389a4864", + "name": "connected", + "displayName": "Connected", + "displayNameEvent": "connected changed", + "defaultValue": false, + "type": "bool" + }, + { + "id": "5bac4ad7-f55c-4301-8d72-f2783d9909ff", + "name": "power", + "displayName": "Power", + "displayNameEvent": "Power changed", + "displayNameAction": "Set power", + "defaultValue": false, + "type": "bool", + "writable": true + }, + { + "id": "bc98cdb0-4d0e-48ca-afc7-922e49bb7813", + "name": "mute", + "displayName": "mute", + "displayNameEvent": "Mute changed", + "displayNameAction": "Set mute", + "type": "bool", + "defaultValue": false, + "writable": true + }, + { + "id": "9dfe5d78-4c3f-497c-bab1-bb9fdf7e93a9", + "name": "volume", + "displayName": "volume", + "displayNameEvent": "volume changed", + "displayNameAction": "Set volume", + "unit": "Percentage", + "type": "int", + "minValue": 0, + "maxValue": 100, + "defaultValue": 50, + "writable": true + }, + { + "id": "2dd512b7-40c2-488e-8d4f-6519edaa6f74", + "name": "playbackStatus", + "displayName": "playback status", + "type": "QString", + "possibleValues": ["Playing", "Paused", "Stopped"], + "defaultValue": "Stopped", + "displayNameEvent": "playback status changed", + "displayNameAction": "set playback status", + "writable": true + }, + { + "id": "f2209fec-cceb-46ad-8189-4caf42166e6b", + "type": "QString", + "name": "title", + "displayName": "Title", + "displayNameEvent": "Title changed", + "defaultValue": "" + }, + { + "id": "8cb920a3-3bf1-4231-92d4-8ac27e7b3d65", + "type": "QString", + "name": "artist", + "displayName": "Artist", + "displayNameEvent": "Artist changed", + "defaultValue": "" + }, + { + "id": "ce399eec-9f6a-4903-9916-0e90e38b255e", + "type": "QString", + "name": "collection", + "displayName": "Collection", + "displayNameEvent": "Collection changed", + "defaultValue": "" + }, + { + "id": "44304c82-c2f6-433b-b62b-815382617d0b", + "type": "QString", + "name": "artwork", + "displayName": "Artwork", + "displayNameEvent": "Artwork changed", + "defaultValue": "" + }, + { + "id": "5913aa2a-629d-4de5-bf44-a4a1f130c118", + "type": "bool", + "name": "shuffle", + "displayName": "Shuffle", + "displayNameEvent": "Shuffle changed", + "displayNameAction": "Set shuffle", + "defaultValue": false, + "writable": true + }, + { + "id": "bc02c28e-3f5d-4de4-b9b5-c0b1576c6e7e", + "type": "QString", + "name": "repeat", + "displayName": "Repeat", + "displayNameEvent": "Repeat changed", + "displayNameAction": "Set repeat", + "possibleValues": ["None", "One", "All"], + "defaultValue": "None", + "writable": true + } + ], + "eventTypes": [ + { + "id": "2535a1eb-7643-4874-98f6-b027fdff6311", + "name": "onPlayerPlay", + "displayName": "player play" + }, + { + "id": "99498b1c-e9c0-480a-9e91-662ee79ba976", + "name": "onPlayerPause", + "displayName": "player pause" + }, + { + "id": "a02ce255-3abb-435d-a92e-7f99c952ecb2", + "name": "onPlayerStop", + "displayName": "player stop" + } + ], + "actionTypes": [ + { + "id": "a180807d-1265-4831-9d86-a421767418dd", + "name": "skipBack", + "displayName": "skip back" + }, + { + "id": "ae3cbe03-ee3e-410e-abbd-efabc2402198", + "name": "stop", + "displayName": "stop" + }, + { + "id": "4d2ee668-a2e3-4795-8b96-0c800b703b46", + "name": "play", + "displayName": "play" + }, + { + "id": "3cf341cb-fe63-40bc-a450-9678d18e91e3", + "name": "pause", + "displayName": "pause" + }, + { + "id": "85d7126a-b123-4a28-aeb4-d84bcfb4d14f", + "name": "skipNext", + "displayName": "skipNext" + } + ] + } + ] + } + ] +}