diff --git a/libnymea-core/devices/devicemanagerimplementation.cpp b/libnymea-core/devices/devicemanagerimplementation.cpp index 91ee8f5f..bb079ad6 100644 --- a/libnymea-core/devices/devicemanagerimplementation.cpp +++ b/libnymea-core/devices/devicemanagerimplementation.cpp @@ -169,7 +169,7 @@ Device::DeviceError DeviceManagerImplementation::setPluginConfig(const PluginId return Device::DeviceErrorPluginNotFound; } - ParamList params = pluginConfig; + ParamList params = buildParams(plugin->configurationDescription(), pluginConfig); Device::DeviceError verify = DeviceUtils::verifyParams(plugin->configurationDescription(), params); if (verify != Device::DeviceErrorNoError) return verify; @@ -248,8 +248,7 @@ DeviceDiscoveryInfo* DeviceManagerImplementation::discoverDevices(const DeviceCl return discoveryInfo; } - // Create a copy of the parameter list because we might modify it (fillig in default values etc) - ParamList effectiveParams = params; + ParamList effectiveParams = buildParams(deviceClass.discoveryParamTypes(), params); Device::DeviceError result = DeviceUtils::verifyParams(deviceClass.discoveryParamTypes(), effectiveParams); if (result != Device::DeviceErrorNoError) { qCWarning(dcDeviceManager) << "Device discovery failed. Parameter verification failed."; @@ -266,7 +265,7 @@ DeviceDiscoveryInfo* DeviceManagerImplementation::discoverDevices(const DeviceCl } qCDebug(dcDeviceManager()) << "Discovery finished. Found devices:" << discoveryInfo->deviceDescriptors().count(); foreach (const DeviceDescriptor &descriptor, discoveryInfo->deviceDescriptors()) { - m_discoveredDevices[descriptor.deviceClassId()].insert(descriptor.id(), descriptor); + m_discoveredDevices.insert(descriptor.id(), descriptor); } }); @@ -280,9 +279,9 @@ DeviceDiscoveryInfo* DeviceManagerImplementation::discoverDevices(const DeviceCl * Optionally you can supply an id yourself if you must keep track of the added device. If you don't supply it, a new one will * be generated. Only devices with \l{DeviceClass}{CreateMethodUser} can be created using this method. * Returns \l{DeviceError} to inform about the result. */ -DeviceSetupInfo* DeviceManagerImplementation::addConfiguredDevice(const DeviceClassId &deviceClassId, const QString &name, const ParamList ¶ms, const DeviceId id) +DeviceSetupInfo* DeviceManagerImplementation::addConfiguredDevice(const DeviceClassId &deviceClassId, const ParamList ¶ms, const QString &name) { - return addConfiguredDeviceInternal(deviceClassId, name, params, id); + return addConfiguredDeviceInternal(deviceClassId, name, params); } /*! Add a new configured device for the given \l{DeviceClass} the given DeviceDescriptorId and \a deviceId. Only devices with \l{DeviceClass}{CreateMethodDiscovery} @@ -291,9 +290,16 @@ DeviceSetupInfo* DeviceManagerImplementation::addConfiguredDevice(const DeviceCl * are used but can be overridden here. * * Returns \l{DeviceError} to inform about the result. */ -DeviceSetupInfo *DeviceManagerImplementation::addConfiguredDevice(const DeviceClassId &deviceClassId, const QString &name, const DeviceDescriptorId &deviceDescriptorId, const ParamList ¶ms, const DeviceId &deviceId) +DeviceSetupInfo *DeviceManagerImplementation::addConfiguredDevice(const DeviceDescriptorId &deviceDescriptorId, const ParamList ¶ms, const QString &name) { - DeviceClass deviceClass = findDeviceClass(deviceClassId); + DeviceDescriptor descriptor = m_discoveredDevices.value(deviceDescriptorId); + if (!descriptor.isValid()) { + DeviceSetupInfo *info = new DeviceSetupInfo(nullptr, this); + info->finish(Device::DeviceErrorDeviceDescriptorNotFound); + return info; + } + + DeviceClass deviceClass = findDeviceClass(descriptor.deviceClassId()); if (!deviceClass.isValid()) { DeviceSetupInfo *info = new DeviceSetupInfo(nullptr, this); info->finish(Device::DeviceErrorDeviceClassNotFound); @@ -305,24 +311,10 @@ DeviceSetupInfo *DeviceManagerImplementation::addConfiguredDevice(const DeviceCl return info; } - DeviceDescriptor descriptor = m_discoveredDevices.value(deviceClassId).value(deviceDescriptorId); - if (!descriptor.isValid()) { - DeviceSetupInfo *info = new DeviceSetupInfo(nullptr, this); - info->finish(Device::DeviceErrorDeviceDescriptorNotFound); - return info; - } + // Merging params from descriptor and user provided ones + ParamList finalParams = buildParams(deviceClass.paramTypes(), params, descriptor.params()); - // Merge params from discovered descriptor and additional overrides provided on API call. User provided params have higher priority than discovery params. - ParamList finalParams; - foreach (const ParamType ¶mType, deviceClass.paramTypes()) { - if (params.hasParam(paramType.id())) { - finalParams.append(Param(paramType.id(), params.paramValue(paramType.id()))); - } else if (descriptor.params().hasParam(paramType.id())) { - finalParams.append(Param(paramType.id(), descriptor.params().paramValue(paramType.id()))); - } - } - - return addConfiguredDeviceInternal(deviceClassId, name, finalParams, deviceId, descriptor.parentDeviceId()); + return addConfiguredDeviceInternal(descriptor.deviceClassId(), name, finalParams, descriptor.parentDeviceId()); } @@ -331,7 +323,7 @@ DeviceSetupInfo *DeviceManagerImplementation::addConfiguredDevice(const DeviceCl * from a discovery or if the user set them. If it came from discovery not writable parameters (readOnly) will be changed too. * * Returns \l{DeviceError} to inform about the result. */ -DeviceSetupInfo* DeviceManagerImplementation::reconfigureDevice(const DeviceId &deviceId, const ParamList ¶ms, bool fromDiscoveryOrAuto) +DeviceSetupInfo* DeviceManagerImplementation::reconfigureDevice(const DeviceId &deviceId, const ParamList ¶ms, const QString &name) { Device *device = findConfiguredDevice(deviceId); if (!device) { @@ -341,7 +333,6 @@ DeviceSetupInfo* DeviceManagerImplementation::reconfigureDevice(const DeviceId & return info; } - 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."; @@ -350,32 +341,69 @@ DeviceSetupInfo* DeviceManagerImplementation::reconfigureDevice(const DeviceId & return info; } - DevicePlugin *plugin = m_devicePlugins.value(deviceClass.pluginId()); + foreach (const ParamType ¶mType, deviceClass.paramTypes()) { + if (paramType.readOnly() && params.hasParam(paramType.id())) { + DeviceSetupInfo *info = new DeviceSetupInfo(nullptr, this); + qCWarning(dcDeviceManager()) << "Parameter" << paramType.name() << paramType.id() << "is not writable"; + info->finish(Device::DeviceErrorParameterNotWritable); + return info; + } + } + + ParamList finalParams = buildParams(deviceClass.paramTypes(), params); + + return reconfigureDeviceInternal(device, finalParams, name); +} + +/*! Edit the \l{Param}{Params} of a configured device to the \l{Param}{Params} of the DeviceDescriptor with the + * given \a deviceId to the given DeviceDescriptorId. + * Only devices with \l{DeviceClass}{CreateMethodDiscovery} can be changed using this method. + * The \a deviceDescriptorId must refer to an existing DeviceDescriptorId from the discovery. + * This method allows to rediscover a device and update it's \l{Param}{Params}. + * + * Returns \l{DeviceError} to inform about the result. */ +DeviceSetupInfo *DeviceManagerImplementation::reconfigureDevice(const DeviceDescriptorId &deviceDescriptorId, const ParamList ¶ms, const QString &name) +{ + DeviceDescriptor descriptor = m_discoveredDevices.value(deviceDescriptorId); + if (!descriptor.isValid()) { + qCWarning(dcDeviceManager()) << "Cannot reconfigure device. No deviceDescriptor with ID" << deviceDescriptorId << "found."; + DeviceSetupInfo *info = new DeviceSetupInfo(nullptr, this); + info->finish(Device::DeviceErrorDeviceDescriptorNotFound); + return info; + } + + Device *device = findConfiguredDevice(descriptor.deviceId()); + if (!device) { + DeviceSetupInfo *info = new DeviceSetupInfo(nullptr, this); + qCWarning(dcDeviceManager()) << "Cannot reconfigure device. No device with ID" << descriptor.deviceId() << "found."; + info->finish(Device::DeviceErrorDeviceNotFound); + return info; + } + + DeviceClass deviceClass = findDeviceClass(device->deviceClassId()); + if (!deviceClass.isValid()) { + qCWarning(dcDeviceManager()) << "Cannot reconfigure device. No deviceClass with ID" << device->deviceClassId() << "found."; + DeviceSetupInfo *info = new DeviceSetupInfo(nullptr, this); + info->finish(Device::DeviceErrorDeviceClassNotFound); + return info; + } + + ParamList finalParams = buildParams(device->deviceClass().paramTypes(), params, descriptor.params()); + return reconfigureDeviceInternal(device, finalParams, name); +} + +DeviceSetupInfo *DeviceManagerImplementation::reconfigureDeviceInternal(Device *device, const ParamList ¶ms, const QString &name) +{ + DevicePlugin *plugin = m_devicePlugins.value(device->deviceClass().pluginId()); if (!plugin) { - qCWarning(dcDeviceManager()) << "Cannot reconfigure device. Plugin for DeviceClass" << deviceClass.displayName() << deviceClass.id().toString() << "not found."; + qCWarning(dcDeviceManager()) << "Cannot reconfigure device. Plugin for DeviceClass" << device->deviceClassId().toString() << "not found."; DeviceSetupInfo *info = new DeviceSetupInfo(nullptr, this); info->finish(Device::DeviceErrorPluginNotFound); return info; } - // if the params are discovered and not set by the user - if (!fromDiscoveryOrAuto) { - // check if one of the given params is not editable - foreach (const ParamType ¶mType, deviceClass.paramTypes()) { - foreach (const Param ¶m, params) { - if (paramType.id() == param.paramTypeId()) { - if (paramType.readOnly()) { - qCWarning(dcDeviceManager()) << "Cannot reconfigure device. Read-only parameters set by user."; - DeviceSetupInfo *info = new DeviceSetupInfo(nullptr, this); - info->finish(Device::DeviceErrorParameterNotWritable); - return info; - } - } - } - } - } - - Device::DeviceError result = DeviceUtils::verifyParams(deviceClass.paramTypes(), effectiveParams, false); + ParamList finalParams = buildParams(device->deviceClass().paramTypes(), params); + Device::DeviceError result = DeviceUtils::verifyParams(device->deviceClass().paramTypes(), finalParams); if (result != Device::DeviceErrorNoError) { qCWarning(dcDeviceManager()) << "Cannot reconfigure device. Params failed validation."; DeviceSetupInfo *info = new DeviceSetupInfo(nullptr, this); @@ -390,10 +418,14 @@ DeviceSetupInfo* DeviceManagerImplementation::reconfigureDevice(const DeviceId & device->setSetupComplete(false); // set new params - foreach (const Param ¶m, effectiveParams) { + foreach (const Param ¶m, params) { device->setParamValue(param.paramTypeId(), param.value()); } + if (!name.isEmpty()) { + device->setName(name); + } + // try to setup the device with the new params DeviceSetupInfo *info = new DeviceSetupInfo(device, this, 30000); plugin->setupDevice(info); @@ -415,44 +447,7 @@ DeviceSetupInfo* DeviceManagerImplementation::reconfigureDevice(const DeviceId & }); return info; -} -/*! Edit the \l{Param}{Params} of a configured device to the \l{Param}{Params} of the DeviceDescriptor with the - * given \a deviceId to the given DeviceDescriptorId. - * Only devices with \l{DeviceClass}{CreateMethodDiscovery} can be changed using this method. - * The \a deviceDescriptorId must refer to an existing DeviceDescriptorId from the discovery. - * This method allows to rediscover a device and update it's \l{Param}{Params}. - * - * Returns \l{DeviceError} to inform about the result. */ -DeviceSetupInfo *DeviceManagerImplementation::reconfigureDevice(const DeviceId &deviceId, const DeviceDescriptorId &deviceDescriptorId) -{ - Device *device = findConfiguredDevice(deviceId); - if (!device) { - DeviceSetupInfo *info = new DeviceSetupInfo(nullptr, this); - info->finish(Device::DeviceErrorDeviceNotFound); - return info; - } - - DeviceClass deviceClass = findDeviceClass(device->deviceClassId()); - if (!deviceClass.isValid()) { - DeviceSetupInfo *info = new DeviceSetupInfo(nullptr, this); - info->finish(Device::DeviceErrorDeviceClassNotFound); - return info; - } - if (!deviceClass.createMethods().testFlag(DeviceClass::CreateMethodDiscovery)) { - DeviceSetupInfo *info = new DeviceSetupInfo(nullptr, this); - info->finish(Device::DeviceErrorCreationMethodNotSupported); - return info; - } - - DeviceDescriptor descriptor = m_discoveredDevices.value(deviceClass.id()).value(deviceDescriptorId); - if (!descriptor.isValid()) { - DeviceSetupInfo *info = new DeviceSetupInfo(nullptr, this); - info->finish(Device::DeviceErrorDeviceDescriptorNotFound); - return info; - } - - return reconfigureDevice(deviceId, descriptor.params(), true); } /*! Edit the \a name of the \l{Device} with the given \a deviceId. @@ -478,57 +473,89 @@ Device::DeviceError DeviceManagerImplementation::setDeviceSettings(const DeviceI qCWarning(dcDeviceManager()) << "Cannot set device settings. Device" << deviceId.toString() << "not found"; return Device::DeviceErrorDeviceNotFound; } - ParamList effectiveSettings = settings; - Device::DeviceError status = DeviceUtils::verifyParams(findDeviceClass(device->deviceClassId()).settingsTypes(), effectiveSettings); + // build a list of settings using: a) provided new settings b) previous settings and c) default values + ParamList effectiveSettings = buildParams(device->deviceClass().settingsTypes(), settings, device->settings()); + Device::DeviceError status = DeviceUtils::verifyParams(device->deviceClass().settingsTypes(), effectiveSettings); if (status != Device::DeviceErrorNoError) { qCWarning(dcDeviceManager()) << "Error setting device settings for device" << device->name() << device->id().toString(); return status; } - foreach (const Param &setting, settings) { - device->setSettingValue(setting.paramTypeId(), setting.value()); - } + device->setSettings(effectiveSettings); return Device::DeviceErrorNoError; } -/*! Initiates a pairing with a \l{DeviceClass}{Device} with the given \a pairingTransactionId, \a deviceClassId, \a name and \a params. - * Returns \l{Device::DeviceError}{DeviceError} to inform about the result. */ -DevicePairingInfo* DeviceManagerImplementation::pairDevice(const DeviceClassId &deviceClassId, const QString &name, const ParamList ¶ms) +DevicePairingInfo* DeviceManagerImplementation::pairDevice(const DeviceClassId &deviceClassId, const ParamList ¶ms, const QString &name) { PairingTransactionId transactionId = PairingTransactionId::createPairingTransactionId(); - // Create new device id - DeviceId newDeviceId = DeviceId::createDeviceId(); - DevicePairingInfo *info = new DevicePairingInfo(transactionId, deviceClassId, newDeviceId, name, params, DeviceId(), this, 30000); - pairDeviceInternal(info); - return info; -} -/*! Initiates a pairing with a \l{DeviceClass}{Device} with the given \a pairingTransactionId, \a deviceClassId, \a name and \a deviceDescriptorId. - * Returns \l{Device::DeviceError}{DeviceError} to inform about the result. */ -DevicePairingInfo* DeviceManagerImplementation::pairDevice(const DeviceClassId &deviceClassId, const QString &name, const DeviceDescriptorId &deviceDescriptorId) -{ - PairingTransactionId pairingTransactionId = PairingTransactionId::createPairingTransactionId(); - DeviceClass deviceClass = findDeviceClass(deviceClassId); - if (deviceClass.id().isNull()) { - qCWarning(dcDeviceManager) << "Cannot find a device class with id" << deviceClassId; - DevicePairingInfo *info = new DevicePairingInfo(pairingTransactionId, deviceClassId, DeviceId(), name, ParamList(), DeviceId(), this); + DeviceClass deviceClass = m_supportedDevices.value(deviceClassId); + if (!deviceClass.isValid()) { + qCWarning(dcDeviceManager) << "Cannot find a DeviceClass with ID" << deviceClassId.toString(); + DevicePairingInfo *info = new DevicePairingInfo(transactionId, deviceClassId, DeviceId(), name, ParamList(), DeviceId(), this); info->finish(Device::DeviceErrorDeviceClassNotFound); return info; } - DeviceDescriptor deviceDescriptor = m_discoveredDevices.value(deviceClassId).value(deviceDescriptorId); - if (!deviceDescriptor.isValid()) { - qCWarning(dcDeviceManager) << "Cannot find a DeviceDescriptor with ID" << deviceClassId.toString(); - DevicePairingInfo *info = new DevicePairingInfo(pairingTransactionId, deviceClassId, DeviceId(), name, ParamList(), DeviceId(), this); + // Create new device id + DeviceId newDeviceId = DeviceId::createDeviceId(); + + // Use given params, if there are missing some, use the defaults ones. + ParamList finalParams = buildParams(deviceClass.paramTypes(), params); + + DevicePairingInfo *info = new DevicePairingInfo(transactionId, deviceClassId, newDeviceId, name, finalParams, DeviceId(), this, 30000); + pairDeviceInternal(info); + return info; +} + +DevicePairingInfo* DeviceManagerImplementation::pairDevice(const DeviceDescriptorId &deviceDescriptorId, const ParamList ¶ms, const QString &name) +{ + PairingTransactionId pairingTransactionId = PairingTransactionId::createPairingTransactionId(); + DeviceDescriptor descriptor = m_discoveredDevices.value(deviceDescriptorId); + if (!descriptor.isValid()) { + qCWarning(dcDeviceManager) << "Cannot find a DeviceDescriptor with ID" << deviceDescriptorId.toString(); + DevicePairingInfo *info = new DevicePairingInfo(pairingTransactionId, DeviceClassId(), DeviceId(), name, ParamList(), DeviceId(), this); info->finish(Device::DeviceErrorDeviceDescriptorNotFound); return info; } - DeviceId deviceId = deviceDescriptor.deviceId(); + DeviceClass deviceClass = m_supportedDevices.value(descriptor.deviceClassId()); + if (!deviceClass.isValid()) { + qCWarning(dcDeviceManager) << "Cannot find a DeviceClass with ID" << descriptor.deviceClassId().toString(); + DevicePairingInfo *info = new DevicePairingInfo(pairingTransactionId, descriptor.deviceClassId(), DeviceId(), name, ParamList(), DeviceId(), this); + info->finish(Device::DeviceErrorDeviceClassNotFound); + return info; + } + + DeviceId deviceId = descriptor.deviceId(); // If it's a new device (not a reconfiguration), create a new DeviceId now. if (deviceId.isNull()) { deviceId = DeviceId::createDeviceId(); } - DevicePairingInfo *info = new DevicePairingInfo(pairingTransactionId, deviceClassId, deviceId, name, deviceDescriptor.params(), deviceDescriptor.parentDeviceId(), this, 30000); + + // Use given params, if there are missing some, use the discovered ones. + ParamList finalParams = buildParams(deviceClass.paramTypes(), params, descriptor.params()); + + DevicePairingInfo *info = new DevicePairingInfo(pairingTransactionId, descriptor.deviceClassId(), deviceId, name, finalParams, descriptor.parentDeviceId(), this, 30000); + pairDeviceInternal(info); + return info; +} + +DevicePairingInfo *DeviceManagerImplementation::pairDevice(const DeviceId &deviceId, const ParamList ¶ms, const QString &name) +{ + PairingTransactionId pairingTransactionId = PairingTransactionId::createPairingTransactionId(); + + Device *device = findConfiguredDevice(deviceId); + if (!device) { + qCWarning(dcDeviceManager) << "Cannot find a Device with ID" << deviceId.toString(); + DevicePairingInfo *info = new DevicePairingInfo(pairingTransactionId, DeviceClassId(), deviceId, name, ParamList(), DeviceId(), this); + info->finish(Device::DeviceErrorDeviceDescriptorNotFound); + return info; + } + + // Use new params, if there are missing some, use the existing ones. + ParamList finalParams = buildParams(device->deviceClass().paramTypes(), params, device->params()); + + DevicePairingInfo *info = new DevicePairingInfo(pairingTransactionId, device->deviceClassId(), deviceId, name, finalParams, DeviceId(), this, 30000); pairDeviceInternal(info); return info; } @@ -599,9 +626,7 @@ DevicePairingInfo *DeviceManagerImplementation::confirmPairing(const PairingTran } device->setParams(internalInfo->params()); - ParamList settings; - // Use verifyParams to populate it with defaults - DeviceUtils::verifyParams(deviceClass.settingsTypes(), settings); + ParamList settings = buildParams(deviceClass.settingsTypes(), ParamList()); device->setSettings(settings); DeviceSetupInfo *info = setupDevice(device); @@ -642,7 +667,7 @@ DevicePairingInfo *DeviceManagerImplementation::confirmPairing(const PairingTran /*! This method will only be used from the DeviceManagerImplementation in order to add a \l{Device} with the given \a deviceClassId, \a name, \a params and \ id. * Returns \l{DeviceError} to inform about the result. */ -DeviceSetupInfo* DeviceManagerImplementation::addConfiguredDeviceInternal(const DeviceClassId &deviceClassId, const QString &name, const ParamList ¶ms, const DeviceId &deviceId, const DeviceId &parentDeviceId) +DeviceSetupInfo* DeviceManagerImplementation::addConfiguredDeviceInternal(const DeviceClassId &deviceClassId, const QString &name, const ParamList ¶ms, const DeviceId &parentDeviceId) { DeviceClass deviceClass = findDeviceClass(deviceClassId); if (deviceClass.id().isNull()) { @@ -657,10 +682,10 @@ DeviceSetupInfo* DeviceManagerImplementation::addConfiguredDeviceInternal(const return info; } - if (m_configuredDevices.contains(deviceId)) { - DeviceSetupInfo *info = new DeviceSetupInfo(nullptr, this); - info->finish(Device::DeviceErrorDuplicateUuid); - return info; + DeviceId deviceId = DeviceId::createDeviceId(); + // Chances are like 0, but... + while (m_configuredDevices.contains(deviceId)) { + deviceId = DeviceId::createDeviceId(); } DevicePlugin *plugin = m_devicePlugins.value(deviceClass.pluginId()); @@ -670,7 +695,8 @@ DeviceSetupInfo* DeviceManagerImplementation::addConfiguredDeviceInternal(const return info; } - ParamList effectiveParams = params; + // set params + ParamList effectiveParams = buildParams(deviceClass.paramTypes(), params); Device::DeviceError paramsResult = DeviceUtils::verifyParams(deviceClass.paramTypes(), effectiveParams); if (paramsResult != Device::DeviceErrorNoError) { DeviceSetupInfo *info = new DeviceSetupInfo(nullptr, this); @@ -687,8 +713,8 @@ DeviceSetupInfo* DeviceManagerImplementation::addConfiguredDeviceInternal(const } device->setParams(effectiveParams); - ParamList settings; - DeviceUtils::verifyParams(deviceClass.settingsTypes(), settings); + // set settings (init with defaults) + ParamList settings = buildParams(deviceClass.settingsTypes(), ParamList()); qCDebug(dcDeviceManager()) << "Adding device settings" << settings << deviceId; device->setSettings(settings); @@ -936,7 +962,7 @@ DeviceActionInfo *DeviceManagerImplementation::executeAction(const Action &actio return info; } - ParamList finalParams = action.params(); + ParamList finalParams = buildParams(actionType.paramTypes(), action.params()); Device::DeviceError paramCheck = DeviceUtils::verifyParams(actionType.paramTypes(), finalParams); if (paramCheck != Device::DeviceErrorNoError) { qCWarning(dcDeviceManager()) << "Cannot execute action. Parameter verification failed."; @@ -1206,13 +1232,9 @@ void DeviceManagerImplementation::loadConfiguredDevices() params.append(Param(ParamTypeId(paramTypeIdString), settings.value(paramTypeIdString))); } } - DeviceUtils::verifyParams(deviceClass.settingsTypes(), deviceSettings); - // Make sure all settings are around. if they aren't initialize with default values - foreach (const ParamType &settingsType, deviceClass.settingsTypes()) { - if (!deviceSettings.hasParam(settingsType.id())) { - deviceSettings.append(Param(settingsType.id(), settingsType.defaultValue().isValid() ? settingsType.defaultValue() : "")); - } - } + + // Fill in any missing params with defaults + deviceSettings = buildParams(deviceClass.settingsTypes(), deviceSettings); device->setSettings(deviceSettings); @@ -1332,7 +1354,8 @@ void DeviceManagerImplementation::onAutoDevicesAppeared(const DeviceDescriptors continue; } qCDebug(dcDeviceManager()) << "Start reconfiguring auto device" << device; - reconfigureDevice(deviceDescriptor.deviceId(), deviceDescriptor.params(), true); + ParamList finalParams = buildParams(deviceClass.paramTypes(), deviceDescriptor.params()); + reconfigureDeviceInternal(device, finalParams); continue; } @@ -1340,8 +1363,7 @@ void DeviceManagerImplementation::onAutoDevicesAppeared(const DeviceDescriptors device->m_autoCreated = true; device->setName(deviceDescriptor.title()); device->setParams(deviceDescriptor.params()); - ParamList settings; - DeviceUtils::verifyParams(deviceClass.settingsTypes(), settings); + ParamList settings = buildParams(deviceClass.settingsTypes(), ParamList()); device->setSettings(settings); device->setParentId(deviceDescriptor.parentDeviceId()); @@ -1453,6 +1475,22 @@ void DeviceManagerImplementation::slotDeviceSettingChanged(const ParamTypeId &pa emit deviceSettingChanged(device->id(), paramTypeId, value); } +ParamList DeviceManagerImplementation::buildParams(const ParamTypes &types, const ParamList &first, const ParamList &second) +{ + // Merge params from discovered descriptor and additional overrides provided on API call. User provided params have higher priority than discovery params. + ParamList finalParams; + foreach (const ParamType ¶mType, types) { + if (first.hasParam(paramType.id())) { + finalParams.append(Param(paramType.id(), first.paramValue(paramType.id()))); + } else if (second.hasParam(paramType.id())) { + finalParams.append(Param(paramType.id(), second.paramValue(paramType.id()))); + } else if (paramType.defaultValue().isValid()){ + finalParams.append(Param(paramType.id(), paramType.defaultValue())); + } + } + return finalParams; +} + void DeviceManagerImplementation::pairDeviceInternal(DevicePairingInfo *info) { DeviceClass deviceClass = m_supportedDevices.value(info->deviceClassId()); diff --git a/libnymea-core/devices/devicemanagerimplementation.h b/libnymea-core/devices/devicemanagerimplementation.h index 2efedaf0..0d40756d 100644 --- a/libnymea-core/devices/devicemanagerimplementation.h +++ b/libnymea-core/devices/devicemanagerimplementation.h @@ -83,19 +83,20 @@ public: DeviceDiscoveryInfo* discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override; - DeviceSetupInfo* addConfiguredDevice(const DeviceClassId &deviceClassId, const QString &name, const ParamList ¶ms, const DeviceId id = DeviceId::createDeviceId()) override; - DeviceSetupInfo* addConfiguredDevice(const DeviceClassId &deviceClassId, const QString &name, const DeviceDescriptorId &deviceDescriptorId, const ParamList ¶ms = ParamList(), const DeviceId &deviceId = DeviceId::createDeviceId()) override; + DeviceSetupInfo* addConfiguredDevice(const DeviceClassId &deviceClassId, const ParamList ¶ms, const QString &name = QString()) override; + DeviceSetupInfo* addConfiguredDevice(const DeviceDescriptorId &deviceDescriptorId, const ParamList ¶ms = ParamList(), const QString &name = QString()) override; - DeviceSetupInfo* reconfigureDevice(const DeviceId &deviceId, const ParamList ¶ms, bool fromDiscoveryOrAuto = false) override; - DeviceSetupInfo* reconfigureDevice(const DeviceId &deviceId, const DeviceDescriptorId &deviceDescriptorId) override; + DeviceSetupInfo* reconfigureDevice(const DeviceId &deviceId, const ParamList ¶ms, const QString &name = QString()) override; + DeviceSetupInfo* reconfigureDevice(const DeviceDescriptorId &deviceDescriptorId, const ParamList ¶ms = ParamList(), const QString &name = QString()) override; + + DevicePairingInfo* pairDevice(const DeviceClassId &deviceClassId, const ParamList ¶ms, const QString &name = QString()) override; + DevicePairingInfo* pairDevice(const DeviceDescriptorId &deviceDescriptorId, const ParamList ¶ms = ParamList(), const QString &name = QString()) override; + DevicePairingInfo* pairDevice(const DeviceId &deviceId, const ParamList ¶ms, const QString &name = QString()) override; + DevicePairingInfo* confirmPairing(const PairingTransactionId &pairingTransactionId, const QString &username, const QString &secret) override; Device::DeviceError editDevice(const DeviceId &deviceId, const QString &name) override; Device::DeviceError setDeviceSettings(const DeviceId &deviceId, const ParamList &settings) override; - DevicePairingInfo* pairDevice(const DeviceClassId &deviceClassId, const QString &name, const ParamList ¶ms) override; - DevicePairingInfo* pairDevice(const DeviceClassId &deviceClassId, const QString &name, const DeviceDescriptorId &deviceDescriptorId) override; - DevicePairingInfo* confirmPairing(const PairingTransactionId &pairingTransactionId, const QString &username, const QString &secret) override; - Device::DeviceError removeConfiguredDevice(const DeviceId &deviceId) override; DeviceActionInfo* executeAction(const Action &action) override; @@ -130,8 +131,12 @@ private slots: void slotDeviceSettingChanged(const ParamTypeId ¶mTypeId, const QVariant &value); private: + // Builds a list of params ready to create a device. + // Template is deviceClass.paramtypes, "first" has highest priority. If a param is not found neither in first nor in second, defaults apply. + ParamList buildParams(const ParamTypes &types, const ParamList &first, const ParamList &second = ParamList()); void pairDeviceInternal(DevicePairingInfo *info); - DeviceSetupInfo *addConfiguredDeviceInternal(const DeviceClassId &deviceClassId, const QString &name, const ParamList ¶ms, const DeviceId &deviceId = DeviceId::createDeviceId(), const DeviceId &parentDeviceId = DeviceId()); + DeviceSetupInfo *addConfiguredDeviceInternal(const DeviceClassId &deviceClassId, const QString &name, const ParamList ¶ms, const DeviceId &parentDeviceId = DeviceId()); + DeviceSetupInfo *reconfigureDeviceInternal(Device *device, const ParamList ¶ms, const QString &name = QString()); DeviceSetupInfo *setupDevice(Device *device); void postSetupDevice(Device *device); void storeDeviceStates(Device *device); @@ -147,7 +152,7 @@ private: QHash > m_vendorDeviceMap; QHash m_supportedDevices; QHash m_configuredDevices; - QHash > m_discoveredDevices; + QHash m_discoveredDevices; QHash m_devicePlugins; diff --git a/libnymea-core/jsonrpc/devicehandler.cpp b/libnymea-core/jsonrpc/devicehandler.cpp index 45c6145f..ea06a221 100644 --- a/libnymea-core/jsonrpc/devicehandler.cpp +++ b/libnymea-core/jsonrpc/devicehandler.cpp @@ -127,7 +127,7 @@ DeviceHandler::DeviceHandler(QObject *parent) : "Devices with CreateMethodJustAdd require all parameters to be supplied here. " "Devices with CreateMethodDiscovery require the use of a deviceDescriptorId. For discovered " "devices params are not required and will be taken from the DeviceDescriptor, however, they " - "may be overridden by supplying parameters here." + "may be overridden by supplying deviceParams." ); params.insert("deviceClassId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); params.insert("name", JsonTypes::basicTypeToString(JsonTypes::String)); @@ -141,28 +141,44 @@ DeviceHandler::DeviceHandler(QObject *parent) : returns.insert("o:displayMessage", JsonTypes::basicTypeToString(JsonTypes::String)); setReturns("AddConfiguredDevice", returns); - returns.clear(); // Reused params from above! + params.clear(); returns.clear(); setDescription("PairDevice", "Pair a device. " - "Use this for DeviceClasses with a setupMethod different than SetupMethodJustAdd. " - "Use deviceDescriptorId or deviceParams, depending on the createMethod of the device class. " - "CreateMethodJustAdd takes the parameters you want to have with that device. " - "CreateMethodDiscovery requires the use of a deviceDescriptorId, optionally, parameters can be overridden here. " + "Use this to set up or reconfigure devices for DeviceClasses with a setupMethod different than SetupMethodJustAdd. " + "Depending on the CreateMethod and whether a new devices is set up or an existing one is reconfigured, different parameters " + "are required:\n" + "CreateMethodJustAdd takes the deviceClassId and the parameters you want to have with that device.\n" + "CreateMethodDiscovery requires the use of a deviceDescriptorId, previously obtained with DiscoverDevices. Optionally, " + "parameters can be overridden with the give deviceParams.\n" + "If an existing device should be reconfigured, the deviceId of said device should be given additionally.\n" "If success is true, the return values will contain a pairingTransactionId, a displayMessage and " - "the setupMethod. Depending on the setupMethod you should either proceed with AddConfiguredDevice " - "or PairDevice." + "the setupMethod. Depending on the setupMethod, the application should present the use an appropriate login mask, " + "that is, For SetupMethodDisplayPin the user should enter a pin that is displayed on the device, for SetupMethodEnterPin the " + "application should present the given PIN so the user can enter it on the device. For SetupMethodPushButton, the displayMessage " + "shall be presented to the user as informational hints to press a button on the device. For SetupMethodUserAndPassword a login " + "mask for a user and password login should be presented to the user. In case of SetupMethodOAuth, an OAuth URL will be returned " + "which shall be opened in a web view to allow the user logging in.\n" + "Once the login procedure has completed, the application shall proceed with ConfirmPairing, providing the results of the pairing " + "procedure." ); + params.insert("o:deviceClassId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); + params.insert("o:name", JsonTypes::basicTypeToString(JsonTypes::String)); + params.insert("o:deviceDescriptorId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); + params.insert("o:deviceParams", deviceParams); + params.insert("o:deviceId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); setParams("PairDevice", params); returns.insert("deviceError", JsonTypes::deviceErrorRef()); returns.insert("o:setupMethod", JsonTypes::setupMethodRef()); returns.insert("o:pairingTransactionId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); returns.insert("o:displayMessage", JsonTypes::basicTypeToString(JsonTypes::String)); returns.insert("o:oAuthUrl", JsonTypes::basicTypeToString(JsonTypes::String)); + returns.insert("o:pin", JsonTypes::basicTypeToString(JsonTypes::String)); setReturns("PairDevice", returns); params.clear(); returns.clear(); setDescription("ConfirmPairing", "Confirm an ongoing pairing. For SetupMethodUserAndPassword, provide the username in the \"username\" field " "and the password in the \"secret\" field. For SetupMethodEnterPin and provide the PIN in the \"secret\" " - "field. For SetupMethodOAuth, return the entire unmodified callback URL containing the code parameter back in the secret field."); + "field. In case of SetupMethodOAuth, the previously opened web view will eventually be redirected to http://128.0.0.1:8888 " + "and the OAuth code as query parameters to this url. Provide the entire unmodified URL in the secret field."); params.insert("pairingTransactionId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); params.insert("o:username", JsonTypes::basicTypeToString(JsonTypes::String)); params.insert("o:secret", JsonTypes::basicTypeToString(JsonTypes::String)); @@ -200,12 +216,14 @@ DeviceHandler::DeviceHandler(QObject *parent) : setReturns("GetDiscoveredDevices", returns); params.clear(); returns.clear(); - setDescription("ReconfigureDevice", "Edit the parameter configuration of the device. The device params will be set to the " - "passed parameters and the setup device will be called. If the device is discoverable, " - "you can perform a GetDiscoveredDevices before calling this method and pass " - "the new DeviceDescriptor (rediscover). Only writable parameters can be changed. By default, " - "every Param is writable."); - params.insert("deviceId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); + setDescription("ReconfigureDevice", "Reconfigure a device. This comes down to removing and recreating a device with new parameters " + "but keeping its device id the same (and with that keeping rules, tags etc). For devices with " + "create method CreateMethodDiscovery, a discovery (GetDiscoveredDevices) shall be performed first " + "and this method is to be called with a deviceDescriptorId of the re-discovered device instead of " + "the deviceId directly. Device parameters will be taken from the discovery, but can be overridden " + "individually here by providing them in the deviceParams parameter. Only writable parameters can " + "be changed."); + params.insert("o:deviceId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); params.insert("o:deviceDescriptorId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); QVariantList newDeviceParams; newDeviceParams.append(JsonTypes::paramRef()); @@ -451,20 +469,19 @@ JsonReply* DeviceHandler::SetPluginConfiguration(const QVariantMap ¶ms) JsonReply* DeviceHandler::AddConfiguredDevice(const QVariantMap ¶ms) { - DeviceClassId deviceClass(params.value("deviceClassId").toString()); + DeviceClassId deviceClassId(params.value("deviceClassId").toString()); QString deviceName = params.value("name").toString(); ParamList deviceParams = JsonTypes::unpackParams(params.value("deviceParams").toList()); DeviceDescriptorId deviceDescriptorId(params.value("deviceDescriptorId").toString()); - DeviceId newDeviceId = DeviceId::createDeviceId(); QLocale locale = params.value("locale").toLocale(); JsonReply *jsonReply = createAsyncReply("AddConfiguredDevice"); DeviceSetupInfo *info; if (deviceDescriptorId.isNull()) { - info = NymeaCore::instance()->deviceManager()->addConfiguredDevice(deviceClass, deviceName, deviceParams, newDeviceId); + info = NymeaCore::instance()->deviceManager()->addConfiguredDevice(deviceClassId, deviceParams, deviceName); } else { - info = NymeaCore::instance()->deviceManager()->addConfiguredDevice(deviceClass, deviceName, deviceDescriptorId, deviceParams, newDeviceId); + info = NymeaCore::instance()->deviceManager()->addConfiguredDevice(deviceDescriptorId, deviceParams, deviceName); } connect(info, &DeviceSetupInfo::finished, jsonReply, [info, jsonReply, locale](){ QVariantMap returns; @@ -486,28 +503,31 @@ JsonReply* DeviceHandler::AddConfiguredDevice(const QVariantMap ¶ms) JsonReply *DeviceHandler::PairDevice(const QVariantMap ¶ms) { - DeviceClassId deviceClassId(params.value("deviceClassId").toString()); QString deviceName = params.value("name").toString(); - DeviceClass deviceClass = NymeaCore::instance()->deviceManager()->findDeviceClass(deviceClassId); + ParamList deviceParams = JsonTypes::unpackParams(params.value("deviceParams").toList()); QLocale locale = params.value("locale").toLocale(); DevicePairingInfo *info; if (params.contains("deviceDescriptorId")) { - DeviceDescriptorId deviceDescriptorId(params.value("deviceDescriptorId").toString()); - info = NymeaCore::instance()->deviceManager()->pairDevice(deviceClassId, deviceName, deviceDescriptorId); + DeviceDescriptorId deviceDescriptorId = DeviceDescriptorId(params.value("deviceDescriptorId").toString()); + info = NymeaCore::instance()->deviceManager()->pairDevice(deviceDescriptorId, deviceParams, deviceName); + } else if (params.contains("deviceId")) { + DeviceId deviceId = DeviceId(params.value("deviceId").toString()); + info = NymeaCore::instance()->deviceManager()->pairDevice(deviceId, deviceParams, deviceName); } else { - ParamList deviceParams = JsonTypes::unpackParams(params.value("deviceParams").toList()); - info = NymeaCore::instance()->deviceManager()->pairDevice(deviceClassId, deviceName, deviceParams); + DeviceClassId deviceClassId(params.value("deviceClassId").toString()); + info = NymeaCore::instance()->deviceManager()->pairDevice(deviceClassId, deviceParams, deviceName); } JsonReply *jsonReply = createAsyncReply("PairDevice"); - connect(info, &DevicePairingInfo::finished, jsonReply, [jsonReply, info, locale, deviceClass](){ + connect(info, &DevicePairingInfo::finished, jsonReply, [jsonReply, info, locale](){ QVariantMap returns; returns.insert("deviceError", JsonTypes::deviceErrorToString(info->status())); returns.insert("pairingTransactionId", info->transactionId().toString()); if (info->status() == Device::DeviceErrorNoError) { + DeviceClass deviceClass = NymeaCore::instance()->deviceManager()->findDeviceClass(info->deviceClassId()); returns.insert("setupMethod", JsonTypes::setupMethodToString(deviceClass.setupMethod())); } @@ -584,10 +604,15 @@ JsonReply *DeviceHandler::ReconfigureDevice(const QVariantMap ¶ms) JsonReply *jsonReply = createAsyncReply("ReconfigureDevice"); DeviceSetupInfo *info; - if (deviceDescriptorId.isNull()) { + if (!deviceDescriptorId.isNull()) { + info = NymeaCore::instance()->deviceManager()->reconfigureDevice(deviceDescriptorId, deviceParams); + } else if (!deviceId.isNull()){ info = NymeaCore::instance()->deviceManager()->reconfigureDevice(deviceId, deviceParams); } else { - info = NymeaCore::instance()->deviceManager()->reconfigureDevice(deviceId, deviceDescriptorId); + qCWarning(dcJsonRpc()) << "Either deviceId or deviceDescriptorId are required"; + QVariantMap ret; + ret.insert("deviceError", JsonTypes::deviceErrorToString(Device::DeviceErrorMissingParameter)); + return createReply(ret); } connect(info, &DeviceSetupInfo::finished, jsonReply, [info, jsonReply, locale](){ @@ -597,6 +622,7 @@ JsonReply *DeviceHandler::ReconfigureDevice(const QVariantMap ¶ms) returns.insert("displayMessage", info->translatedDisplayMessage(locale)); jsonReply->setData(returns); jsonReply->finished(); + }); return jsonReply; diff --git a/libnymea-core/servers/rest/devicesresource.cpp b/libnymea-core/servers/rest/devicesresource.cpp index f74e5864..19f05523 100644 --- a/libnymea-core/servers/rest/devicesresource.cpp +++ b/libnymea-core/servers/rest/devicesresource.cpp @@ -375,7 +375,6 @@ HttpReply *DevicesResource::addConfiguredDevice(const QByteArray &payload) const return createDeviceErrorReply(HttpReply::BadRequest, Device::DeviceErrorDeviceClassNotFound); QString deviceName = params.value("name").toString(); - DeviceId newDeviceId = DeviceId::createDeviceId(); ParamList deviceParams = JsonTypes::unpackParams(params.value("deviceParams").toList()); DeviceDescriptorId deviceDescriptorId(params.value("deviceDescriptorId").toString()); @@ -384,10 +383,10 @@ HttpReply *DevicesResource::addConfiguredDevice(const QByteArray &payload) const DeviceSetupInfo *info; if (deviceDescriptorId.isNull()) { qCDebug(dcRest) << "Adding device" << deviceName << "with" << deviceParams; - info = NymeaCore::instance()->deviceManager()->addConfiguredDevice(deviceClassId, deviceName, deviceParams, newDeviceId); + info = NymeaCore::instance()->deviceManager()->addConfiguredDevice(deviceClassId, deviceParams, deviceName); } else { qCDebug(dcRest) << "Adding discovered device" << deviceName << "with DeviceDescriptorId" << deviceDescriptorId.toString(); - info = NymeaCore::instance()->deviceManager()->addConfiguredDevice(deviceClassId, deviceName, deviceDescriptorId, deviceParams, newDeviceId); + info = NymeaCore::instance()->deviceManager()->addConfiguredDevice(deviceDescriptorId, deviceParams, deviceName); } connect(info, &DeviceSetupInfo::finished, httpReply, [info, httpReply](){ @@ -443,6 +442,7 @@ HttpReply *DevicesResource::pairDevice(const QByteArray &payload) const } QString deviceName = params.value("name").toString(); + ParamList deviceParams = JsonTypes::unpackParams(params.value("deviceParams").toList()); qCDebug(dcRest) << "Pair device" << deviceName << "with deviceClassId" << deviceClassId.toString(); @@ -451,10 +451,9 @@ HttpReply *DevicesResource::pairDevice(const QByteArray &payload) const DevicePairingInfo *info; if (params.contains("deviceDescriptorId")) { DeviceDescriptorId deviceDescriptorId(params.value("deviceDescriptorId").toString()); - info = NymeaCore::instance()->deviceManager()->pairDevice(deviceClassId, deviceName, deviceDescriptorId); + info = NymeaCore::instance()->deviceManager()->pairDevice(deviceDescriptorId, deviceParams, deviceName); } else { - ParamList deviceParams = JsonTypes::unpackParams(params.value("deviceParams").toList()); - info = NymeaCore::instance()->deviceManager()->pairDevice(deviceClassId, deviceName, deviceParams); + info = NymeaCore::instance()->deviceManager()->pairDevice(deviceClassId, deviceParams, deviceName); } connect(info, &DevicePairingInfo::finished, httpReply, [info, httpReply](){ @@ -533,7 +532,7 @@ HttpReply *DevicesResource::reconfigureDevice(Device *device, const QByteArray & info = NymeaCore::instance()->deviceManager()->reconfigureDevice(device->id(), deviceParams); } else { qCDebug(dcRest) << "Reconfigure device using the new discovered device with descriptorId:" << deviceDescriptorId.toString(); - info = NymeaCore::instance()->deviceManager()->reconfigureDevice(device->id(), deviceDescriptorId); + info = NymeaCore::instance()->deviceManager()->reconfigureDevice(deviceDescriptorId); } connect(info, &DeviceSetupInfo::finished, httpReply, [httpReply, info](){ diff --git a/libnymea/devices/devicemanager.h b/libnymea/devices/devicemanager.h index a77140c1..2d55f80c 100644 --- a/libnymea/devices/devicemanager.h +++ b/libnymea/devices/devicemanager.h @@ -101,19 +101,20 @@ public: virtual DeviceDiscoveryInfo* discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) = 0; - virtual DeviceSetupInfo* addConfiguredDevice(const DeviceClassId &deviceClassId, const QString &name, const ParamList ¶ms, const DeviceId id = DeviceId::createDeviceId()) = 0; - virtual DeviceSetupInfo* addConfiguredDevice(const DeviceClassId &deviceClassId, const QString &name, const DeviceDescriptorId &deviceDescriptorId, const ParamList ¶ms = ParamList(), const DeviceId &deviceId = DeviceId::createDeviceId()) = 0; + virtual DeviceSetupInfo* addConfiguredDevice(const DeviceClassId &deviceClassId, const ParamList ¶ms, const QString &name = QString()) = 0; + virtual DeviceSetupInfo* addConfiguredDevice(const DeviceDescriptorId &deviceDescriptorId, const ParamList ¶ms = ParamList(), const QString &name = QString()) = 0; - virtual DeviceSetupInfo* reconfigureDevice(const DeviceId &deviceId, const ParamList ¶ms, bool fromDiscoveryOrAuto = false) = 0; - virtual DeviceSetupInfo* reconfigureDevice(const DeviceId &deviceId, const DeviceDescriptorId &deviceDescriptorId) = 0; + virtual DeviceSetupInfo* reconfigureDevice(const DeviceId &deviceId, const ParamList ¶ms, const QString &name = QString()) = 0; + virtual DeviceSetupInfo* reconfigureDevice(const DeviceDescriptorId &deviceDescriptorId, const ParamList ¶ms = ParamList(), const QString &name = QString()) = 0; + + virtual DevicePairingInfo* pairDevice(const DeviceClassId &deviceClassId, const ParamList ¶ms, const QString &name = QString()) = 0; + virtual DevicePairingInfo* pairDevice(const DeviceDescriptorId &deviceDescriptorId, const ParamList ¶ms = ParamList(), const QString &name = QString()) = 0; + virtual DevicePairingInfo* pairDevice(const DeviceId &deviceId, const ParamList ¶ms, const QString &name = QString()) = 0; + virtual DevicePairingInfo* confirmPairing(const PairingTransactionId &pairingTransactionId, const QString &username = QString(), const QString &secret = QString()) = 0; virtual Device::DeviceError editDevice(const DeviceId &deviceId, const QString &name) = 0; virtual Device::DeviceError setDeviceSettings(const DeviceId &deviceId, const ParamList &settings) = 0; - virtual DevicePairingInfo* pairDevice(const DeviceClassId &deviceClassId, const QString &name, const ParamList ¶ms) = 0; - virtual DevicePairingInfo* pairDevice(const DeviceClassId &deviceClassId, const QString &name, const DeviceDescriptorId &deviceDescriptorId) = 0; - virtual DevicePairingInfo* confirmPairing(const PairingTransactionId &pairingTransactionId, const QString &username = QString(), const QString &secret = QString()) = 0; - virtual Device::DeviceError removeConfiguredDevice(const DeviceId &deviceId) = 0; virtual DeviceActionInfo* executeAction(const Action &action) = 0; diff --git a/libnymea/devices/deviceutils.cpp b/libnymea/devices/deviceutils.cpp index 70f6b22d..07126c91 100644 --- a/libnymea/devices/deviceutils.cpp +++ b/libnymea/devices/deviceutils.cpp @@ -34,7 +34,7 @@ DeviceUtils::DeviceUtils() /*! Verify if the given \a params matches the given \a paramTypes. Ith \a requireAll * is true, all \l{ParamList}{Params} has to be valid. Returns \l{Device::DeviceError} to inform about the result.*/ -Device::DeviceError DeviceUtils::verifyParams(const QList paramTypes, ParamList ¶ms, bool requireAll) +Device::DeviceError DeviceUtils::verifyParams(const QList paramTypes, const ParamList ¶ms) { foreach (const Param ¶m, params) { Device::DeviceError result = verifyParam(paramTypes, param); @@ -42,25 +42,17 @@ Device::DeviceError DeviceUtils::verifyParams(const QList paramTypes, return result; } } - if (!requireAll) { - return Device::DeviceErrorNoError; - } foreach (const ParamType ¶mType, paramTypes) { bool found = false; foreach (const Param ¶m, params) { if (paramType.id() == param.paramTypeId()) { found = true; + break; } } - // This paramType has a default value... lets fill in that one. - if (!paramType.defaultValue().isNull() && !found) { - found = true; - params.append(Param(paramType.id(), paramType.defaultValue())); - } - if (!found) { - qCWarning(dcDevice) << "Missing parameter:" << paramType.name(); + qCWarning(dcDevice) << "Missing parameter:" << paramType.name() << params; return Device::DeviceErrorMissingParameter; } } diff --git a/libnymea/devices/deviceutils.h b/libnymea/devices/deviceutils.h index 6416d255..ca8d018f 100644 --- a/libnymea/devices/deviceutils.h +++ b/libnymea/devices/deviceutils.h @@ -34,7 +34,7 @@ class DeviceUtils public: DeviceUtils(); - static Device::DeviceError verifyParams(const QList paramTypes, ParamList ¶ms, bool requireAll = true); + static Device::DeviceError verifyParams(const QList paramTypes, const ParamList ¶ms); static Device::DeviceError verifyParam(const QList paramTypes, const Param ¶m); static Device::DeviceError verifyParam(const ParamType ¶mType, const Param ¶m); diff --git a/nymea.pri b/nymea.pri index b4413341..b59ee388 100644 --- a/nymea.pri +++ b/nymea.pri @@ -3,7 +3,7 @@ NYMEA_VERSION_STRING=$$system('dpkg-parsechangelog | sed -n -e "s/^Version: //p" # define protocol versions JSON_PROTOCOL_VERSION_MAJOR=3 -JSON_PROTOCOL_VERSION_MINOR=1 +JSON_PROTOCOL_VERSION_MINOR=2 REST_API_VERSION=1 LIBNYMEA_API_VERSION_MAJOR=3 LIBNYMEA_API_VERSION_MINOR=0 diff --git a/tests/auto/api.json b/tests/auto/api.json index 13cd845a..b703132d 100644 --- a/tests/auto/api.json +++ b/tests/auto/api.json @@ -1,4 +1,4 @@ -3.1 +3.2 { "methods": { "Actions.ExecuteAction": { @@ -260,7 +260,7 @@ } }, "Devices.AddConfiguredDevice": { - "description": "Add a configured device with a setupMethod of SetupMethodJustAdd. For devices with a setupMethod different than SetupMethodJustAdd, use PairDevice. Devices with CreateMethodJustAdd require all parameters to be supplied here. Devices with CreateMethodDiscovery require the use of a deviceDescriptorId. For discovered devices params are not required and will be taken from the DeviceDescriptor, however, they may be overridden by supplying parameters here.", + "description": "Add a configured device with a setupMethod of SetupMethodJustAdd. For devices with a setupMethod different than SetupMethodJustAdd, use PairDevice. Devices with CreateMethodJustAdd require all parameters to be supplied here. Devices with CreateMethodDiscovery require the use of a deviceDescriptorId. For discovered devices params are not required and will be taken from the DeviceDescriptor, however, they may be overridden by supplying deviceParams.", "params": { "deviceClassId": "Uuid", "name": "String", @@ -289,7 +289,7 @@ } }, "Devices.ConfirmPairing": { - "description": "Confirm an ongoing pairing. For SetupMethodUserAndPassword, provide the username in the \"username\" field and the password in the \"secret\" field. For SetupMethodEnterPin and provide the PIN in the \"secret\" field. For SetupMethodOAuth, return the entire unmodified callback URL containing the code parameter back in the secret field.", + "description": "Confirm an ongoing pairing. For SetupMethodUserAndPassword, provide the username in the \"username\" field and the password in the \"secret\" field. For SetupMethodEnterPin and provide the PIN in the \"secret\" field. In case of SetupMethodOAuth, the previously opened web view will eventually be redirected to http://128.0.0.1:8888 and the OAuth code as query parameters to this url. Provide the entire unmodified URL in the secret field.", "params": { "o:secret": "String", "o:username": "String", @@ -452,28 +452,30 @@ } }, "Devices.PairDevice": { - "description": "Pair a device. Use this for DeviceClasses with a setupMethod different than SetupMethodJustAdd. Use deviceDescriptorId or deviceParams, depending on the createMethod of the device class. CreateMethodJustAdd takes the parameters you want to have with that device. CreateMethodDiscovery requires the use of a deviceDescriptorId, optionally, parameters can be overridden here. If success is true, the return values will contain a pairingTransactionId, a displayMessage and the setupMethod. Depending on the setupMethod you should either proceed with AddConfiguredDevice or PairDevice.", + "description": "Pair a device. Use this to set up or reconfigure devices for DeviceClasses with a setupMethod different than SetupMethodJustAdd. Depending on the CreateMethod and whether a new devices is set up or an existing one is reconfigured, different parameters are required:\nCreateMethodJustAdd takes the deviceClassId and the parameters you want to have with that device.\nCreateMethodDiscovery requires the use of a deviceDescriptorId, previously obtained with DiscoverDevices. Optionally, parameters can be overridden with the give deviceParams.\nIf an existing device should be reconfigured, the deviceId of said device should be given additionally.\nIf success is true, the return values will contain a pairingTransactionId, a displayMessage and the setupMethod. Depending on the setupMethod, the application should present the use an appropriate login mask, that is, For SetupMethodDisplayPin the user should enter a pin that is displayed on the device, for SetupMethodEnterPin the application should present the given PIN so the user can enter it on the device. For SetupMethodPushButton, the displayMessage shall be presented to the user as informational hints to press a button on the device. For SetupMethodUserAndPassword a login mask for a user and password login should be presented to the user. In case of SetupMethodOAuth, an OAuth URL will be returned which shall be opened in a web view to allow the user logging in.\nOnce the login procedure has completed, the application shall proceed with ConfirmPairing, providing the results of the pairing procedure.", "params": { - "deviceClassId": "Uuid", - "name": "String", + "o:deviceClassId": "Uuid", "o:deviceDescriptorId": "Uuid", + "o:deviceId": "Uuid", "o:deviceParams": [ "$ref:Param" - ] + ], + "o:name": "String" }, "returns": { "deviceError": "$ref:DeviceError", "o:displayMessage": "String", "o:oAuthUrl": "String", "o:pairingTransactionId": "Uuid", + "o:pin": "String", "o:setupMethod": "$ref:SetupMethod" } }, "Devices.ReconfigureDevice": { - "description": "Edit the parameter configuration of the device. The device params will be set to the passed parameters and the setup device will be called. If the device is discoverable, you can perform a GetDiscoveredDevices before calling this method and pass the new DeviceDescriptor (rediscover). Only writable parameters can be changed. By default, every Param is writable.", + "description": "Reconfigure a device. This comes down to removing and recreating a device with new parameters but keeping its device id the same (and with that keeping rules, tags etc). For devices with create method CreateMethodDiscovery, a discovery (GetDiscoveredDevices) shall be performed first and this method is to be called with a deviceDescriptorId of the re-discovered device instead of the deviceId directly. Device parameters will be taken from the discovery, but can be overridden individually here by providing them in the deviceParams parameter. Only writable parameters can be changed.", "params": { - "deviceId": "Uuid", "o:deviceDescriptorId": "Uuid", + "o:deviceId": "Uuid", "o:deviceParams": [ "$ref:Param" ] diff --git a/tests/auto/devices/testdevices.cpp b/tests/auto/devices/testdevices.cpp index 58b041b3..33120650 100644 --- a/tests/auto/devices/testdevices.cpp +++ b/tests/auto/devices/testdevices.cpp @@ -338,13 +338,18 @@ void TestDevices::addConfiguredDevice_data() QVariantMap fakeparam; fakeparam.insert("paramTypeId", ParamTypeId::createParamTypeId()); invalidDeviceParams.append(fakeparam); - QTest::newRow("User, JustAdd, invalid param") << mockDeviceClassId << invalidDeviceParams << Device::DeviceErrorInvalidParameter; + QTest::newRow("User, JustAdd, invalid param") << mockDeviceClassId << invalidDeviceParams << Device::DeviceErrorMissingParameter; - fakeparam.insert("value", "buhuu"); + QVariantMap fakeparam2; + fakeparam2.insert("paramTypeId", mockDeviceHttpportParamTypeId.toString()); + fakeparam2.insert("value", "blabla"); invalidDeviceParams.clear(); - invalidDeviceParams.append(fakeparam); + invalidDeviceParams.append(fakeparam2); QTest::newRow("User, JustAdd, wrong param") << mockDeviceClassId << invalidDeviceParams << Device::DeviceErrorInvalidParameter; + deviceParams.clear(); deviceParams << httpportParam << fakeparam; + QTest::newRow("USer, JustAdd, additional invalid param") << mockDeviceClassId << deviceParams << Device::DeviceErrorNoError; + } void TestDevices::addConfiguredDevice() @@ -1198,6 +1203,7 @@ void TestDevices::reconfigureByDiscovery() QFETCH(Device::DeviceError, error); QFETCH(QVariantList, discoveryParams); + qCDebug(dcTests()) << "Discovering..."; QVariantMap params; params.insert("deviceClassId", deviceClassId); params.insert("discoveryParams", discoveryParams); @@ -1211,31 +1217,33 @@ void TestDevices::reconfigureByDiscovery() // add Discovered Device 1 port 55555 QVariantList deviceDescriptors = response.toMap().value("params").toMap().value("deviceDescriptors").toList(); - DeviceDescriptorId descriptorId1; + DeviceDescriptorId descriptorId; foreach (const QVariant &descriptor, deviceDescriptors) { // find the device with port 55555 if (descriptor.toMap().value("description").toString() == "55555") { - descriptorId1 = DeviceDescriptorId(descriptor.toMap().value("id").toString()); - qDebug() << descriptorId1.toString(); + descriptorId = DeviceDescriptorId(descriptor.toMap().value("id").toString()); + qDebug() << descriptorId.toString(); break; } } - qDebug() << "adding descriptorId 1" << descriptorId1; + QVERIFY(!descriptorId.isNull()); - QVERIFY(!descriptorId1.isNull()); + qCDebug(dcTests()) << "Adding..."; params.clear(); response.clear(); params.insert("deviceClassId", deviceClassId); params.insert("name", "Discoverd mock device"); - params.insert("deviceDescriptorId", descriptorId1); + params.insert("deviceDescriptorId", descriptorId); response = injectAndWait("Devices.AddConfiguredDevice", params); DeviceId deviceId(response.toMap().value("params").toMap().value("deviceId").toString()); QVERIFY(!deviceId.isNull()); - // and now rediscover, and edit the first device with the second + // and now rediscover and find the existing device in the discovery results + qCDebug(dcTests()) << "Re-Discovering..."; + params.clear(); response.clear(); params.insert("deviceClassId", deviceClassId); @@ -1247,24 +1255,28 @@ void TestDevices::reconfigureByDiscovery() QCOMPARE(response.toMap().value("params").toMap().value("deviceDescriptors").toList().count(), resultCount); } - // get the second device - DeviceDescriptorId descriptorId2; + deviceDescriptors = response.toMap().value("params").toMap().value("deviceDescriptors").toList(); + + // find the already added device + descriptorId = DeviceDescriptorId(); // reset it first foreach (const QVariant &descriptor, deviceDescriptors) { - // find the device with port 55556 - if (descriptor.toMap().value("description").toString() == "55556") { - descriptorId2 = DeviceDescriptorId(descriptor.toMap().value("id").toString()); + if (descriptor.toMap().value("deviceId").toUuid().toString() == deviceId.toString()) { + descriptorId = DeviceDescriptorId(descriptor.toMap().value("id").toString()); break; } } - QVERIFY(!descriptorId2.isNull()); + QVERIFY2(!descriptorId.isNull(), QString("Device %1 not found in discovery results: %2").arg(deviceId.toString()).arg(qUtf8Printable(QJsonDocument::fromVariant(response).toJson())).toUtf8()); - qDebug() << "edit device 1 (55555) with descriptor 2 (55556) " << descriptorId2; + qCDebug(dcTests()) << "Reconfiguring..."; - // EDIT response.clear(); params.clear(); - params.insert("deviceId", deviceId.toString()); - params.insert("deviceDescriptorId", descriptorId2); + params.insert("deviceDescriptorId", descriptorId); + // override port param + QVariantMap portParam; + portParam.insert("paramTypeId", mockDeviceHttpportParamTypeId); + portParam.insert("value", "55556"); + params.insert("deviceParams", QVariantList() << portParam); response = injectAndWait("Devices.ReconfigureDevice", params); verifyDeviceError(response, error); @@ -1601,7 +1613,7 @@ void TestDevices::discoverDeviceParenting() DeviceDescriptorId descriptorId = discoveryInfo->deviceDescriptors().first().id(); QSignalSpy addSpy(NymeaCore::instance()->deviceManager(), &DeviceManager::deviceAdded); - DeviceSetupInfo *setupInfo = NymeaCore::instance()->deviceManager()->addConfiguredDevice(mockParentDeviceClassId, "Mock Parent (Discovered)", descriptorId); + DeviceSetupInfo *setupInfo = NymeaCore::instance()->deviceManager()->addConfiguredDevice(descriptorId, ParamList(), "Mock Parent (Discovered)"); { QSignalSpy spy(setupInfo, &DeviceSetupInfo::finished); spy.wait(); @@ -1627,7 +1639,7 @@ void TestDevices::discoverDeviceParenting() // Found one! Adding it... addSpy.clear(); - setupInfo = NymeaCore::instance()->deviceManager()->addConfiguredDevice(mockChildDeviceClassId, "Mock Child (Discovered)", descriptorId); + setupInfo = NymeaCore::instance()->deviceManager()->addConfiguredDevice(descriptorId, ParamList(), "Mock Child (Discovered)"); { QSignalSpy spy(setupInfo, &DeviceSetupInfo::finished); spy.wait(); diff --git a/tests/auto/restdevices/testrestdevices.cpp b/tests/auto/restdevices/testrestdevices.cpp index 285f404d..e43a43c2 100644 --- a/tests/auto/restdevices/testrestdevices.cpp +++ b/tests/auto/restdevices/testrestdevices.cpp @@ -732,19 +732,19 @@ void TestRestDevices::reconfigureByDiscovery() // add Discovered Device 1 port 55555 request.setUrl(QUrl("https://localhost:3333/api/v1/devices")); request.setHeader(QNetworkRequest::ContentTypeHeader, "text/json"); - DeviceDescriptorId descriptorId1; + DeviceDescriptorId descriptorId; foreach (const QVariant &descriptor, foundDevices) { // find the device with port 55555 if (descriptor.toMap().value("description").toString() == "55555") { - descriptorId1 = DeviceDescriptorId(descriptor.toMap().value("id").toString()); - qDebug() << descriptorId1.toString(); + descriptorId = DeviceDescriptorId(descriptor.toMap().value("id").toString()); + qDebug() << descriptorId.toString(); break; } } params.clear(); params.insert("deviceClassId", deviceClassId); params.insert("name", "Discovered mock device"); - params.insert("deviceDescriptorId", descriptorId1.toString()); + params.insert("deviceDescriptorId", descriptorId.toString()); QVariant response = postAndWait(request, params, expectedStatusCode); QVERIFY2(!response.isNull(), "Could not delete device"); @@ -767,23 +767,27 @@ void TestRestDevices::reconfigureByDiscovery() foundDevices = response.toList(); QCOMPARE(foundDevices.count(), resultCount); - // get the second device - DeviceDescriptorId descriptorId2; + // find the already added device + descriptorId = DeviceDescriptorId(); // reset it first foreach (const QVariant &descriptor, foundDevices) { - // find the device with port 55556 - if (descriptor.toMap().value("description").toString() == "55556") { - descriptorId2 = DeviceDescriptorId(descriptor.toMap().value("id").toString()); + if (descriptor.toMap().value("deviceId").toUuid().toString() == deviceId.toString()) { + descriptorId = DeviceDescriptorId(descriptor.toMap().value("id").toString()); break; } } - QVERIFY(!descriptorId2.isNull()); - qDebug() << "edit device 1 (55555) with descriptor 2 (55556) " << descriptorId2; + + QVERIFY(!descriptorId.isNull()); + qDebug() << "edit device 1 (55555) with descriptor 2 (55556) " << descriptorId; // RECONFIGURE response.clear(); params.clear(); - params.insert("deviceId", deviceId.toString()); - params.insert("deviceDescriptorId", descriptorId2); + params.insert("deviceDescriptorId", descriptorId); + // override port param + QVariantMap portParam; + portParam.insert("paramTypeId", mockDeviceHttpportParamTypeId); + portParam.insert("value", "55556"); + params.insert("deviceParams", QVariantList() << portParam); request = QNetworkRequest(QUrl(QString("https://localhost:3333/api/v1/devices/%1").arg(deviceId.toString()))); request.setHeader(QNetworkRequest::ContentTypeHeader, "text/json");