rework how auto devices are created

also updated plugins to work with new api
This commit is contained in:
Michael Zanetti 2014-06-24 00:16:29 +02:00
parent 1c00b4c3f9
commit ac77fc669c
13 changed files with 135 additions and 92 deletions

View File

@ -106,7 +106,7 @@ DeviceManager::DeviceManager(QObject *parent) :
// Give hardware a chance to start up before loading plugins etc. // Give hardware a chance to start up before loading plugins etc.
QMetaObject::invokeMethod(this, "loadPlugins", Qt::QueuedConnection); QMetaObject::invokeMethod(this, "loadPlugins", Qt::QueuedConnection);
QMetaObject::invokeMethod(this, "loadConfiguredDevices", Qt::QueuedConnection); QMetaObject::invokeMethod(this, "loadConfiguredDevices", Qt::QueuedConnection);
QMetaObject::invokeMethod(this, "createNewAutoDevices", Qt::QueuedConnection); QMetaObject::invokeMethod(this, "startMonitoringAutoDevices", Qt::QueuedConnection);
// Make sure this is always emitted after plugins and devices are loaded // Make sure this is always emitted after plugins and devices are loaded
QMetaObject::invokeMethod(this, "loaded", Qt::QueuedConnection); QMetaObject::invokeMethod(this, "loaded", Qt::QueuedConnection);
} }
@ -149,7 +149,6 @@ QPair<DeviceManager::DeviceError, QString> DeviceManager::setPluginConfig(const
} }
settings.endGroup(); settings.endGroup();
settings.endGroup(); settings.endGroup();
createNewAutoDevices();
return result; return result;
} }
@ -540,6 +539,7 @@ void DeviceManager::loadPlugins()
connect(pluginIface, &DevicePlugin::deviceSetupFinished, this, &DeviceManager::slotDeviceSetupFinished); connect(pluginIface, &DevicePlugin::deviceSetupFinished, this, &DeviceManager::slotDeviceSetupFinished);
connect(pluginIface, &DevicePlugin::actionExecutionFinished, this, &DeviceManager::actionExecutionFinished); connect(pluginIface, &DevicePlugin::actionExecutionFinished, this, &DeviceManager::actionExecutionFinished);
connect(pluginIface, &DevicePlugin::pairingFinished, this, &DeviceManager::slotPairingFinished); connect(pluginIface, &DevicePlugin::pairingFinished, this, &DeviceManager::slotPairingFinished);
connect(pluginIface, &DevicePlugin::autoDevicesAppeared, this, &DeviceManager::autoDevicesAppeared);
} }
} }
} }
@ -595,36 +595,10 @@ void DeviceManager::storeConfiguredDevices()
settings.endGroup(); settings.endGroup();
} }
void DeviceManager::createNewAutoDevices() void DeviceManager::startMonitoringAutoDevices()
{ {
bool haveNewDevice = false; foreach (DevicePlugin *plugin, m_devicePlugins) {
foreach (const DeviceClass &deviceClass, m_supportedDevices) { plugin->startMonitoringAutoDevices();
if (deviceClass.createMethod() != DeviceClass::CreateMethodAuto) {
continue;
}
qDebug() << "found auto device class" << deviceClass.name();
DevicePlugin *plugin = m_devicePlugins.value(deviceClass.pluginId());
bool success = false;
do {
QList<Device*> loadedDevices = findConfiguredDevices(deviceClass.id());
Device *device = new Device(plugin->pluginId(), DeviceId::createDeviceId(), deviceClass.id());
success = plugin->configureAutoDevice(loadedDevices, device);
if (success) {
qDebug() << "New device detected for" << deviceClass.name() << device->name();
haveNewDevice = true;
// We'll always add auto devices, even if setup fails in order to keep track of them.
QPair<DeviceSetupStatus, QString> setupStatus = setupDevice(device);
m_configuredDevices.append(device);
} else {
qDebug() << "No newly detected devices for" << deviceClass.name();
delete device;
}
} while (success);
}
if (haveNewDevice) {
storeConfiguredDevices();
} }
} }
@ -770,6 +744,41 @@ void DeviceManager::slotPairingFinished(const QUuid &pairingTransactionId, Devic
emit deviceSetupFinished(device, DeviceError::DeviceErrorNoError, QString()); emit deviceSetupFinished(device, DeviceError::DeviceErrorNoError, QString());
} }
void DeviceManager::autoDevicesAppeared(const DeviceClassId &deviceClassId, const QList<DeviceDescriptor> &deviceDescriptors)
{
DeviceClass deviceClass = findDeviceClass(deviceClassId);
if (!deviceClass.isValid()) {
return;
}
DevicePlugin *plugin = m_devicePlugins.value(deviceClass.pluginId());
if (!plugin) {
return;
}
foreach (const DeviceDescriptor &deviceDescriptor, deviceDescriptors) {
Device *device = new Device(plugin->pluginId(), deviceClassId, this);
device->setName(deviceClass.name());
device->setParams(deviceDescriptor.params());
QPair<DeviceSetupStatus, QString> setupStatus = setupDevice(device);
switch (setupStatus.first) {
case DeviceSetupStatusFailure:
qWarning() << "Device setup failed. Not adding device to system.";
emit deviceSetupFinished(device, DeviceError::DeviceErrorSetupFailed, QString("Device setup failed: %1").arg(setupStatus.second));
delete device;
break;
case DeviceSetupStatusAsync:
break;
case DeviceSetupStatusSuccess:
qDebug() << "Device setup complete.";
emit deviceSetupFinished(device, DeviceError::DeviceErrorNoError, QString());
m_configuredDevices.append(device);
storeConfiguredDevices();
break;
}
}
}
void DeviceManager::slotDeviceStateValueChanged(const QUuid &stateTypeId, const QVariant &value) void DeviceManager::slotDeviceStateValueChanged(const QUuid &stateTypeId, const QVariant &value)
{ {
Device *device = qobject_cast<Device*>(sender()); Device *device = qobject_cast<Device*>(sender());

View File

@ -108,10 +108,11 @@ private slots:
void loadPlugins(); void loadPlugins();
void loadConfiguredDevices(); void loadConfiguredDevices();
void storeConfiguredDevices(); void storeConfiguredDevices();
void createNewAutoDevices(); void startMonitoringAutoDevices();
void slotDevicesDiscovered(const DeviceClassId &deviceClassId, const QList<DeviceDescriptor> deviceDescriptors); void slotDevicesDiscovered(const DeviceClassId &deviceClassId, const QList<DeviceDescriptor> deviceDescriptors);
void slotDeviceSetupFinished(Device *device, DeviceManager::DeviceSetupStatus status, const QString &errorMessage); void slotDeviceSetupFinished(Device *device, DeviceManager::DeviceSetupStatus status, const QString &errorMessage);
void slotPairingFinished(const QUuid &pairingTransactionId, DeviceManager::DeviceSetupStatus status, const QString &errorMessage); void slotPairingFinished(const QUuid &pairingTransactionId, DeviceManager::DeviceSetupStatus status, const QString &errorMessage);
void autoDevicesAppeared(const DeviceClassId &deviceClassId, const QList<DeviceDescriptor> &deviceDescriptors);
// Only connect this to Devices. It will query the sender() // Only connect this to Devices. It will query the sender()
void slotDeviceStateValueChanged(const QUuid &stateTypeId, const QVariant &value); void slotDeviceStateValueChanged(const QUuid &stateTypeId, const QVariant &value);

View File

@ -127,24 +127,14 @@ DevicePlugin::~DevicePlugin()
/*! Override this if your plugin supports Device with DeviceClass::CreationMethodAuto. /*! Override this if your plugin supports Device with DeviceClass::CreationMethodAuto.
This will be called at startup, after the configured devices have been loaded. This will be called at startup, after the configured devices have been loaded.
You should walk through loadedDevices and check whether all the detected devices This is the earliest time you should start emitting autoDevicesAppeared(). If you
are contained in there. If all the detected devices are already contained, return are monitoring some hardware/service for devices to appear, start monitoring now.
false. If instead you've found a new device which isn't known to the system yet, If you are building the devices based on a static list, you may emit
fill in the parameters of the passed device with some details that makes it possible autoDevicesAppeard() in here.
for you to match this Device object with the detected hardware. After that, return true.
The DeviceManager will then insert the device into its database and call setupDevice()
for this device. Therefore you should not do any hardware initialisation in this state yet
but rather wait for the subsequent setupDevice() call to set it up like in any other
case where Device can be created.
Returning false will cause the passed device object to be destroyed.
If you have detected multiple new devices, just load them one by one. The DeviceManager
will continue to call this method until you return false.
*/ */
bool DevicePlugin::configureAutoDevice(QList<Device*> loadedDevices, Device *device) const void DevicePlugin::startMonitoringAutoDevices()
{ {
Q_UNUSED(loadedDevices)
Q_UNUSED(device)
return false;
} }
/*! Reimplement this if you support a DeviceClass with createMethod CreateMethodDiscovery. /*! Reimplement this if you support a DeviceClass with createMethod CreateMethodDiscovery.

View File

@ -49,7 +49,7 @@ public:
virtual QList<DeviceClass> supportedDevices() const = 0; virtual QList<DeviceClass> supportedDevices() const = 0;
virtual DeviceManager::HardwareResources requiredHardware() const = 0; virtual DeviceManager::HardwareResources requiredHardware() const = 0;
virtual bool configureAutoDevice(QList<Device *> loadedDevices, Device *device) const; virtual void startMonitoringAutoDevices();
virtual DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const QList<Param> &params) const; virtual DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const QList<Param> &params) const;
virtual QPair<DeviceManager::DeviceSetupStatus, QString> setupDevice(Device *device); virtual QPair<DeviceManager::DeviceSetupStatus, QString> setupDevice(Device *device);

View File

@ -89,23 +89,35 @@ DeviceManager::HardwareResources DevicePluginBoblight::requiredHardware() const
return DeviceManager::HardwareResourceNone; return DeviceManager::HardwareResourceNone;
} }
bool DevicePluginBoblight::configureAutoDevice(QList<Device *> loadedDevices, Device *device) const void DevicePluginBoblight::startMonitoringAutoDevices()
{ {
if (!m_bobClient->connected()) { if (!m_bobClient->connected()) {
return false; return;
} }
if (loadedDevices.count() < m_bobClient->lightsCount()) {
int index = loadedDevices.count(); QList<Device*> loadedDevices = deviceManager()->findConfiguredDevices(boblightDeviceClassId);
device->setName("Boblight Channel " + QString::number(index));
QList<DeviceDescriptor> deviceDescriptorList;
for (int i = loadedDevices.count(); i < m_bobClient->lightsCount(); i++) {
DeviceDescriptor deviceDescriptor(boblightDeviceClassId, "Boblight Channel " + QString::number(i));
QList<Param> params; QList<Param> params;
Param param("channel"); Param param("channel");
param.setValue(index); param.setValue(i);
params.append(param); params.append(param);
device->setParams(params); deviceDescriptor.setParams(params);
device->setStateValue(colorStateTypeId, m_bobClient->currentColor(index)); deviceDescriptorList.append(deviceDescriptor);
return true;
} }
return false; emit autoDevicesAppeared(boblightDeviceClassId, deviceDescriptorList);
}
QPair<DeviceManager::DeviceSetupStatus, QString> DevicePluginBoblight::setupDevice(Device *device)
{
if (!m_bobClient->connected()) {
return reportDeviceSetup(DeviceManager::DeviceSetupStatusFailure, "Cannot connect to Boblight");
}
m_bobClient->currentColor(device->paramValue("channel").toInt());
return reportDeviceSetup();
} }
QString DevicePluginBoblight::pluginName() const QString DevicePluginBoblight::pluginName() const

View File

@ -39,7 +39,8 @@ public:
QList<DeviceClass> supportedDevices() const override; QList<DeviceClass> supportedDevices() const override;
DeviceManager::HardwareResources requiredHardware() const override; DeviceManager::HardwareResources requiredHardware() const override;
bool configureAutoDevice(QList<Device *> loadedDevices, Device *device) const override; void startMonitoringAutoDevices() override;
QPair<DeviceManager::DeviceSetupStatus, QString> setupDevice(Device *device) override;
QString pluginName() const override; QString pluginName() const override;
PluginId pluginId() const override; PluginId pluginId() const override;

View File

@ -257,21 +257,19 @@ void DevicePluginMock::deviceRemoved(Device *device)
delete m_daemons.take(device); delete m_daemons.take(device);
} }
bool DevicePluginMock::configureAutoDevice(QList<Device *> loadedDevices, Device *device) const void DevicePluginMock::startMonitoringAutoDevices()
{ {
Q_ASSERT(device->deviceClassId() == mockDeviceAutoClassId); DeviceDescriptor mockDescriptor(mockDeviceAutoClassId, "Mock Device (Auto created)");
// We only want to have one auto mock device. So if there's already anything in loadedDevices, don't crearte a new one.
if (loadedDevices.count() > 0) {
return false;
}
device->setName("Mock Device (Auto created)");
QList<Param> params; QList<Param> params;
Param param("httpport", 4242); Param param("httpport", 4242);
params.append(param); params.append(param);
device->setParams(params); mockDescriptor.setParams(params);
return true;
QList<DeviceDescriptor> deviceDescriptorList;
deviceDescriptorList.append(mockDescriptor);
emit autoDevicesAppeared(mockDeviceAutoClassId, deviceDescriptorList);
} }
QList<ParamType> DevicePluginMock::configurationDescription() const QList<ParamType> DevicePluginMock::configurationDescription() const

View File

@ -47,7 +47,7 @@ public:
QPair<DeviceManager::DeviceSetupStatus, QString> setupDevice(Device *device) override; QPair<DeviceManager::DeviceSetupStatus, QString> setupDevice(Device *device) override;
void deviceRemoved(Device *device) override; void deviceRemoved(Device *device) override;
bool configureAutoDevice(QList<Device *> loadedDevices, Device *device) const override; void startMonitoringAutoDevices() override;
QList<ParamType> configurationDescription() const override; QList<ParamType> configurationDescription() const override;

View File

@ -31,6 +31,7 @@ VendorId hueVendorId = VendorId("0ae1e001-2aa6-47ed-b8c0-334c3728a68f");
PluginId huePluginUuid = PluginId("5f2e634b-b7f3-48ee-976a-b5ae22aa5c55"); PluginId huePluginUuid = PluginId("5f2e634b-b7f3-48ee-976a-b5ae22aa5c55");
DeviceClassId hueDeviceClassId = DeviceClassId("d8f4c397-e05e-47c1-8917-8e72d4d0d47c"); DeviceClassId hueDeviceClassId = DeviceClassId("d8f4c397-e05e-47c1-8917-8e72d4d0d47c");
DeviceClassId hueDeviceClassAutoId = DeviceClassId("9cce5981-50a1-4873-a374-c53c095feb3b");
StateTypeId hueColorStateTypeId = StateTypeId("d25423e7-b924-4b20-80b6-77eecc65d089"); StateTypeId hueColorStateTypeId = StateTypeId("d25423e7-b924-4b20-80b6-77eecc65d089");
ActionTypeId hueSetColorActionTypeId = ActionTypeId("29cc299a-818b-47b2-817f-c5a6361545e4"); ActionTypeId hueSetColorActionTypeId = ActionTypeId("29cc299a-818b-47b2-817f-c5a6361545e4");
@ -114,6 +115,20 @@ QList<DeviceClass> DevicePluginPhilipsHue::supportedDevices() const
ret.append(deviceClassHue); ret.append(deviceClassHue);
// Now create the same device again with CreateMethodAuto
// When we pair a bridge, one discovered device is created.
// The other light bulbs connected to the bridge will
// then appear as auto devices.
DeviceClass deviceClassHueAuto(pluginId(), hueVendorId, hueDeviceClassAutoId);
deviceClassHueAuto.setName("Hue");
deviceClassHueAuto.setCreateMethod(DeviceClass::CreateMethodAuto);
deviceClassHueAuto.setParamTypes(paramTypes);
deviceClassHueAuto.setStateTypes(hueStates);
deviceClassHueAuto.setActions(hueActons);
ret.append(deviceClassHueAuto);
return ret; return ret;
} }
@ -122,23 +137,10 @@ DeviceManager::HardwareResources DevicePluginPhilipsHue::requiredHardware() cons
return DeviceManager::HardwareResourceNone; return DeviceManager::HardwareResourceNone;
} }
bool DevicePluginPhilipsHue::configureAutoDevice(QList<Device *> loadedDevices, Device *device) const void DevicePluginPhilipsHue::startMonitoringAutoDevices()
{ {
// if (!m_bobClient->connected()) { // TODO: We could call the bridge to discover new light bulbs here maybe?
// return false; // Although we maybe want to think of a user triggered approach to do such things.
// }
// if (loadedDevices.count() < m_bobClient->lightsCount()) {
// int index = loadedDevices.count();
// device->setName("Boblight Channel " + QString::number(index));
// QList<Param> params;
// Param param("channel");
// param.setValue(index);
// params.append(param);
// device->setParams(params);
// device->setStateValue(colorStateTypeId, m_bobClient->currentColor(index));
// return true;
// }
return false;
} }
QString DevicePluginPhilipsHue::pluginName() const QString DevicePluginPhilipsHue::pluginName() const
@ -189,6 +191,23 @@ QPair<DeviceManager::DeviceSetupStatus, QString> DevicePluginPhilipsHue::setupDe
m_lights.insert(light, device); m_lights.insert(light, device);
m_asyncSetups.insert(light, device); m_asyncSetups.insert(light, device);
// If we have more unconfigured lights around, lets add them as auto devices
QList<DeviceDescriptor> descriptorList;
while (!m_unconfiguredLights.isEmpty()) {
Light *light = m_unconfiguredLights.takeFirst();
DeviceDescriptor descriptor(hueDeviceClassAutoId, light->name());
QList<Param> params;
params.append(Param("number", light->id()));
params.append(Param("ip", light->ip().toString()));
params.append(Param("username", light->username()));
descriptor.setParams(params);
descriptorList.append(descriptor);
}
if (!descriptorList.isEmpty()) {
qDebug() << "adding" << descriptorList.count() << "autodevices";
metaObject()->invokeMethod(this, "autoDevicesAppeared", Qt::QueuedConnection, Q_ARG(DeviceClassId, hueDeviceClassAutoId), Q_ARG(QList<DeviceDescriptor>, descriptorList));
}
return reportDeviceSetup(DeviceManager::DeviceSetupStatusAsync); return reportDeviceSetup(DeviceManager::DeviceSetupStatusAsync);
} }
@ -296,10 +315,10 @@ void DevicePluginPhilipsHue::getLightsFinished(int id, const QVariant &params)
emit pairingFinished(pairingInfo.pairingTransactionId, DeviceManager::DeviceSetupStatusSuccess, QString()); emit pairingFinished(pairingInfo.pairingTransactionId, DeviceManager::DeviceSetupStatusSuccess, QString());
// // If we have more than one device on that bridge, tell DeviceManager that there are more. // If we have more than one device on that bridge, tell DeviceManager that there are more.
// if (params.count() > 1) { if (params.toMap().count() > 1) {
// emit autoDevicesAppeared(); // emit autoDevicesAppeared();
// } }
} }
void DevicePluginPhilipsHue::getFinished(int id, const QVariant &params) void DevicePluginPhilipsHue::getFinished(int id, const QVariant &params)

View File

@ -40,7 +40,7 @@ public:
QList<DeviceClass> supportedDevices() const override; QList<DeviceClass> supportedDevices() const override;
DeviceManager::HardwareResources requiredHardware() const override; DeviceManager::HardwareResources requiredHardware() const override;
bool configureAutoDevice(QList<Device *> loadedDevices, Device *device) const override; void startMonitoringAutoDevices() override;
QString pluginName() const override; QString pluginName() const override;
PluginId pluginId() const override; PluginId pluginId() const override;

View File

@ -40,6 +40,16 @@ Light::Light(const QHostAddress &ip, const QString &username, int id, QObject *p
{ {
} }
QHostAddress Light::ip() const
{
return m_ip;
}
QString Light::username() const
{
return m_username;
}
int Light::id() const int Light::id() const
{ {
return m_id; return m_id;

View File

@ -39,6 +39,9 @@ public:
Light(const QHostAddress &ip, const QString &username, int id, QObject *parent = 0); Light(const QHostAddress &ip, const QString &username, int id, QObject *parent = 0);
QHostAddress ip() const;
QString username() const;
int id() const; int id() const;
QString name() const; QString name() const;

View File

@ -165,7 +165,7 @@ def discover_device(deviceClassId = None):
params = {} params = {}
params['deviceClassId'] = deviceClassId params['deviceClassId'] = deviceClassId
discoveryParams = read_params(deviceClass['paramTypes']) discoveryParams = read_params(deviceClass['discoveryParamTypes'])
if len(discoveryParams) > 0: if len(discoveryParams) > 0:
params['discoveryParams'] = discoveryParams params['discoveryParams'] = discoveryParams