diff --git a/libnymea/devicemanager.cpp b/libnymea/devicemanager.cpp index be059f81..1959e60f 100644 --- a/libnymea/devicemanager.cpp +++ b/libnymea/devicemanager.cpp @@ -469,17 +469,20 @@ DeviceManager::DeviceError DeviceManager::reconfigureDevice(const DeviceId &devi { Device *device = findConfiguredDevice(deviceId); if (!device) { + qCWarning(dcDeviceManager()) << "Cannot reconfigure device. Device with id" << deviceId.toString() << "not found."; return DeviceErrorDeviceNotFound; } ParamList effectiveParams = params; DeviceClass deviceClass = findDeviceClass(device->deviceClassId()); if (deviceClass.id().isNull()) { + qCWarning(dcDeviceManager()) << "Cannot reconfigure device. DeviceClass for device" << device->name() << deviceId.toString() << "not found."; return DeviceErrorDeviceClassNotFound; } DevicePlugin *plugin = m_devicePlugins.value(deviceClass.pluginId()); if (!plugin) { + qCWarning(dcDeviceManager()) << "Cannot reconfigure device. Plugin for DeviceClass" << deviceClass.displayName() << deviceClass.id().toString() << "not found."; return DeviceErrorPluginNotFound; } @@ -489,8 +492,10 @@ DeviceManager::DeviceError DeviceManager::reconfigureDevice(const DeviceId &devi foreach (const ParamType ¶mType, deviceClass.paramTypes()) { foreach (const Param ¶m, params) { if (paramType.id() == param.paramTypeId()) { - if (paramType.readOnly()) + if (paramType.readOnly()) { + qCWarning(dcDeviceManager()) << "Cannot reconfigure device. Read-only parameters set by user."; return DeviceErrorParameterNotWritable; + } } } } @@ -498,6 +503,7 @@ DeviceManager::DeviceError DeviceManager::reconfigureDevice(const DeviceId &devi DeviceError result = verifyParams(deviceClass.paramTypes(), effectiveParams, false); if (result != DeviceErrorNoError) { + qCWarning(dcDeviceManager()) << "Cannot reconfigure device. Params failed validation."; return result; } @@ -1398,7 +1404,22 @@ void DeviceManager::onAutoDevicesAppeared(const DeviceClassId &deviceClassId, co qCWarning(dcDeviceManager()) << "Invalid parent device id. Not adding device to the system."; continue; } - Device *device = new Device(plugin->pluginId(), deviceClassId, this); + + Device *device = nullptr; + + // If the appreaed auto device holds a valid device id, do a reconfiguration for this device + if (!deviceDescriptor.deviceId().isNull()) { + device = findConfiguredDevice(deviceDescriptor.deviceId()); + if (!device) { + qCWarning(dcDeviceManager()) << "Could not find device for auto device descriptor" << deviceDescriptor.deviceId(); + continue; + } + qCDebug(dcDeviceManager()) << "Start reconfiguring auto device" << device; + reconfigureDevice(deviceDescriptor.deviceId(), deviceDescriptor.params()); + continue; + } + + device = new Device(plugin->pluginId(), deviceClassId, this); device->m_autoCreated = true; device->setName(deviceDescriptor.title()); device->setParams(deviceDescriptor.params()); @@ -1452,7 +1473,7 @@ void DeviceManager::onAutoDeviceDisappeared(const DeviceId &deviceId) void DeviceManager::onLoaded() { - qCWarning(dcDeviceManager()) << "Done loading plugins and devices."; + qCDebug(dcDeviceManager()) << "Done loading plugins and devices."; emit loaded(); // schedule some housekeeping... diff --git a/libnymea/plugin/device.cpp b/libnymea/plugin/device.cpp index 70a4f55d..d47e8b63 100644 --- a/libnymea/plugin/device.cpp +++ b/libnymea/plugin/device.cpp @@ -260,3 +260,30 @@ Device *Devices::findById(const DeviceId &id) } return nullptr; } + +QDebug operator<<(QDebug dbg, Device *device) +{ + dbg.nospace() << "Device(" << device->name(); + dbg.nospace() << ", id" << device->id(); + dbg.nospace() << ", deviceClassId" << device->deviceClassId() << ")"; + return dbg.space(); +} + +/*! Filter a Devices list by a parameter. Only Devices having a parameter of the given + \a paramTypeId will be returned. If \a value is given and it is not null, only Devices + with the given \a paramTypeId and the same \a value will be returned. + */ +Devices Devices::filterByParam(const ParamTypeId ¶mTypeId, const QVariant &value) +{ + Devices ret; + foreach (Device* device, *this) { + if (paramTypeId != paramTypeId) { + continue; + } + if (!value.isNull() && device->paramValue(paramTypeId) != value) { + continue; + } + ret << device; + } + return ret; +} diff --git a/libnymea/plugin/device.h b/libnymea/plugin/device.h index 0bac6471..12f8803f 100644 --- a/libnymea/plugin/device.h +++ b/libnymea/plugin/device.h @@ -94,12 +94,15 @@ private: bool m_autoCreated = false; }; -class Devices: public QList +QDebug operator<<(QDebug dbg, Device *device); + +class LIBNYMEA_EXPORT Devices: public QList { public: Devices() = default; Devices(const QList &other); Device* findById(const DeviceId &id); + Devices filterByParam(const ParamTypeId ¶mTypeId, const QVariant &value = QVariant()); }; #endif diff --git a/plugins/mock/devicepluginmock.cpp b/plugins/mock/devicepluginmock.cpp index 7671bb4a..667a9058 100644 --- a/plugins/mock/devicepluginmock.cpp +++ b/plugins/mock/devicepluginmock.cpp @@ -110,6 +110,7 @@ DeviceManager::DeviceSetupStatus DevicePluginMock::setupDevice(Device *device) connect(daemon, &HttpDaemon::setState, this, &DevicePluginMock::setState); // Keep this queued or it might happen that the HttpDaemon is deleted before it is able to reply to the caller connect(daemon, &HttpDaemon::disappear, this, &DevicePluginMock::onDisappear, Qt::QueuedConnection); + connect(daemon, &HttpDaemon::reconfigureAutodevice, this, &DevicePluginMock::onReconfigureAutoDevice, Qt::QueuedConnection); if (async) { m_asyncSetupDevices.append(device); @@ -383,6 +384,30 @@ void DevicePluginMock::onDisappear() emit autoDeviceDisappeared(device->id()); } +void DevicePluginMock::onReconfigureAutoDevice() +{ + HttpDaemon *daemon = qobject_cast(sender()); + if (!daemon) + return; + + Device *device = m_daemons.key(daemon); + qCDebug(dcMockDevice()) << "Reconfigure auto device for" << device << device->params(); + + int currentPort = device->params().paramValue(mockDeviceAutoDeviceHttpportParamTypeId).toInt(); + + // Note: the reconfigure makes the http server listen on port + 1 + ParamList params; + params.append(Param(mockDeviceAutoDeviceHttpportParamTypeId, currentPort + 1)); + + DeviceDescriptor deviceDescriptor; + deviceDescriptor.setTitle(device->name() + " (reconfigured)"); + deviceDescriptor.setDescription("This auto device was reconfigured"); + deviceDescriptor.setDeviceId(device->id()); + deviceDescriptor.setParams(params); + + emit autoDevicesAppeared(mockDeviceAutoDeviceClassId, { deviceDescriptor }); +} + void DevicePluginMock::emitDevicesDiscovered() { QList deviceDescriptors; diff --git a/plugins/mock/devicepluginmock.h b/plugins/mock/devicepluginmock.h index 72e782dd..8efe6d25 100644 --- a/plugins/mock/devicepluginmock.h +++ b/plugins/mock/devicepluginmock.h @@ -59,6 +59,7 @@ private slots: void setState(const StateTypeId &stateTypeId, const QVariant &value); void triggerEvent(const EventTypeId &id); void onDisappear(); + void onReconfigureAutoDevice(); void emitDevicesDiscovered(); void emitPushButtonDevicesDiscovered(); void emitDisplayPinDevicesDiscovered(); diff --git a/plugins/mock/httpdaemon.cpp b/plugins/mock/httpdaemon.cpp index 1aa3f727..c0799b7b 100644 --- a/plugins/mock/httpdaemon.cpp +++ b/plugins/mock/httpdaemon.cpp @@ -114,6 +114,9 @@ void HttpDaemon::readClient() } else if (url.path() == "/disappear") { qCDebug(dcMockDevice) << "Should disappear"; emit disappear(); + } else if (url.path() == "/reconfigureautodevice") { + qCDebug(dcMockDevice) << "Reconfigure auto device"; + emit reconfigureAutodevice(); } if (tokens[0] == "GET") { diff --git a/plugins/mock/httpdaemon.h b/plugins/mock/httpdaemon.h index ea86ae46..277bb04a 100644 --- a/plugins/mock/httpdaemon.h +++ b/plugins/mock/httpdaemon.h @@ -47,6 +47,7 @@ signals: void setState(const StateTypeId &stateTypeId, const QVariant &value); void triggerEvent(const EventTypeId &eventTypeId); void disappear(); + void reconfigureAutodevice(); private slots: void readClient(); diff --git a/tests/auto/devices/testdevices.cpp b/tests/auto/devices/testdevices.cpp index 39087bff..3c1abbd7 100644 --- a/tests/auto/devices/testdevices.cpp +++ b/tests/auto/devices/testdevices.cpp @@ -91,6 +91,7 @@ private slots: void reconfigureByDiscovery(); void reconfigureByDiscoveryAndPair(); + void reconfigureAutodevice(); void removeDevice_data(); void removeDevice(); @@ -1275,6 +1276,34 @@ void TestDevices::reconfigureByDiscoveryAndPair() } +void TestDevices::reconfigureAutodevice() +{ + qCDebug(dcTests()) << "Reconfigure auto device"; + + // Get the autodevice + QList devices = NymeaCore::instance()->deviceManager()->findConfiguredDevices(mockDeviceAutoClassId); + QVERIFY2(devices.count() > 0, "There needs to be at least one auto-created Mock Device for this test"); + + // Get current auto device infos + Device *currentDevice = devices.first(); + DeviceId deviceId = currentDevice->id(); + int currentPort = currentDevice->paramValue(httpportParamTypeId).toInt(); + + // Trigger reconfigure signal in mock device + QNetworkAccessManager *nam = new QNetworkAccessManager(this); + QSignalSpy spy(nam, SIGNAL(finished(QNetworkReply*))); + QNetworkReply *reply = nam->get(QNetworkRequest(QUrl(QString("http://localhost:%1/reconfigureautodevice").arg(currentPort)))); + spy.wait(); + QCOMPARE(spy.count(), 1); + reply->deleteLater(); + + Device *device = NymeaCore::instance()->deviceManager()->findConfiguredDevice(deviceId); + QVERIFY(device); + int newPort = device->paramValue(httpportParamTypeId).toInt(); + // Note: reconfigure autodevice increases the http port by 1 + QVERIFY(newPort == currentPort + 1); +} + void TestDevices::removeDevice_data() {