Fix group action executions

Fixes: #522
This commit is contained in:
Michael Zanetti 2021-01-31 14:41:20 +01:00
parent 1a5d9ec2b9
commit 2c9d5ea4db
8 changed files with 91 additions and 42 deletions

View File

@ -36,7 +36,6 @@
ThingGroup::ThingGroup(DeviceManager *deviceManager, DeviceClass *deviceClass, DevicesProxy *devices, QObject *parent): ThingGroup::ThingGroup(DeviceManager *deviceManager, DeviceClass *deviceClass, DevicesProxy *devices, QObject *parent):
Device(deviceManager, deviceClass, QUuid::createUuid(), parent), Device(deviceManager, deviceClass, QUuid::createUuid(), parent),
m_thingManager(deviceManager),
m_things(devices) m_things(devices)
{ {
deviceClass->setParent(this); deviceClass->setParent(this);
@ -45,7 +44,7 @@ ThingGroup::ThingGroup(DeviceManager *deviceManager, DeviceClass *deviceClass, D
for (int i = 0; i < deviceClass->stateTypes()->rowCount(); i++) { for (int i = 0; i < deviceClass->stateTypes()->rowCount(); i++) {
StateType *st = deviceClass->stateTypes()->get(i); StateType *st = deviceClass->stateTypes()->get(i);
State *state = new State(id(), st->id(), QVariant(), this); State *state = new State(id(), st->id(), QVariant(), this);
qDebug() << "Adding state" << st->name(); qDebug() << "Adding state" << st->name() << st->minValue() << st->maxValue();
states->addState(state); states->addState(state);
} }
setStates(states); setStates(states);
@ -56,14 +55,15 @@ ThingGroup::ThingGroup(DeviceManager *deviceManager, DeviceClass *deviceClass, D
syncStates(); syncStates();
}); });
connect(m_thingManager, &DeviceManager::executeActionReply, this, [this](int commandId, const QVariantMap &/*params*/){ connect(m_thingManager, &DeviceManager::executeActionReply, this, [this](int commandId, const QVariantMap &params){
// This should maybe check the params and only emit NoError if there is at least one thing that returned without error? // This should maybe check the params and create a sensible group result instead of just forwarding the result of the last reply
foreach (int id, m_pendingActions.keys()) { qDebug() << "action reply:" << commandId;
if (m_pendingActions.value(id).contains(commandId)) { foreach (int id, m_pendingGroupActions.keys()) {
m_pendingActions[id].removeAll(commandId); if (m_pendingGroupActions.value(id).contains(commandId)) {
if (m_pendingActions[id].isEmpty()) { m_pendingGroupActions[id].removeAll(commandId);
m_pendingActions.remove(id); if (m_pendingGroupActions[id].isEmpty()) {
emit actionExecutionFinished(id, "DeviceErrorNoError"); m_pendingGroupActions.remove(id);
emit executeActionReply(id, params);
} }
return; return;
} }
@ -76,35 +76,51 @@ int ThingGroup::executeAction(const QString &actionName, const QVariantList &par
{ {
QList<int> pendingIds; QList<int> pendingIds;
ActionType *groupActionType = m_thingClass->actionTypes()->findByName(actionName);
if (!groupActionType) {
qWarning() << "Group has no action" << actionName;
return -1;
}
qDebug() << "Execute action for group:" << this; qDebug() << "Execute action for group:" << this;
for (int i = 0; i < m_things->rowCount(); i++) { for (int i = 0; i < m_things->rowCount(); i++) {
Device *device = m_things->get(i); Device *thing = m_things->get(i);
if (device->setupStatus() != Device::ThingSetupStatusComplete) { if (thing->setupStatus() != Device::ThingSetupStatusComplete) {
continue; continue;
} }
ActionType *actionType = device->thingClass()->actionTypes()->findByName(actionName); ActionType *actionType = thing->thingClass()->actionTypes()->findByName(actionName);
if (!actionType) { if (!actionType) {
qWarning() << "Cannot send action to thing" << thing->name() << "because according action can't be found";
continue; continue;
} }
QVariantList finalParams; QVariantList finalParams;
foreach (const QVariant &paramVariant, params) { foreach (const QVariant &paramVariant, params) {
ParamType *paramType = actionType->paramTypes()->findByName(paramVariant.toMap().value("paramName").toString()); QString paramName = paramVariant.toMap().value("paramName").toString();
if (paramType) { ParamType *groupParamType = groupActionType->paramTypes()->findByName(paramName);
QVariantMap finalParam; if (!groupParamType) {
finalParam.insert("paramTypeId", paramType->id()); qWarning() << "Not adding param" << paramName << "to action" << actionName << "because group action param can't be found";
finalParam.insert("value", paramVariant.toMap().value("value")); continue;
finalParams.append(finalParam);
} }
ParamType *paramType = actionType->paramTypes()->findByName(paramName);
if (!paramType) {
qWarning() << "Not adding param" << paramName << "to action" << actionName << "because according action params can't be found";
continue;
}
QVariantMap finalParam;
finalParam.insert("paramTypeId", paramType->id());
finalParam.insert("value", mapValue(paramVariant.toMap().value("value"), groupParamType, paramType));
finalParams.append(finalParam);
} }
qDebug() << "Initial params" << params; qDebug() << "Initial params" << params;
qDebug() << "Executing" << device->id() << actionType->name() << finalParams; qDebug() << "Executing" << thing->id() << actionType->name() << finalParams;
int id = m_thingManager->executeAction(device->id(), actionType->id(), finalParams); int id = m_thingManager->executeAction(thing->id(), actionType->id(), finalParams);
pendingIds.append(id); pendingIds.append(id);
} }
m_pendingActions.insert(++m_idCounter, pendingIds); m_pendingGroupActions.insert(++m_idCounter, pendingIds);
return m_idCounter; return m_idCounter;
} }
@ -114,7 +130,7 @@ void ThingGroup::syncStates()
StateType *stateType = thingClass()->stateTypes()->get(i); StateType *stateType = thingClass()->stateTypes()->get(i);
State *state = states()->getState(stateType->id()); State *state = states()->getState(stateType->id());
qDebug() << "syncing state" << stateType->name(); qDebug() << "syncing state" << stateType->name() << stateType->type();
QVariant value; QVariant value;
int count = 0; int count = 0;
@ -142,6 +158,9 @@ void ThingGroup::syncStates()
} else if (stateType->type().toLower() == "int") { } else if (stateType->type().toLower() == "int") {
value = value.toInt() + d->stateValue(ds->id()).toInt(); value = value.toInt() + d->stateValue(ds->id()).toInt();
count++; count++;
} else if (stateType->type().toLower() == "qcolor") {
value = d->stateValue(ds->id());
break;
} }
} }
if (count > 0) { if (count > 0) {
@ -150,3 +169,23 @@ void ThingGroup::syncStates()
state->setValue(value); state->setValue(value);
} }
} }
QVariant ThingGroup::mapValue(const QVariant &value, ParamType *fromParamType, ParamType *toParamType) const
{
qDebug() << "Mapping:" << value << fromParamType->minValue() << fromParamType->maxValue() << toParamType->minValue() << toParamType->maxValue();
if (!fromParamType->minValue().isValid()
|| !fromParamType->maxValue().isValid()
|| !toParamType->minValue().isValid()
|| !toParamType->maxValue().isValid()) {
return value;
}
double fromMin = fromParamType->minValue().toDouble();
double fromMax = fromParamType->maxValue().toDouble();
double toMin = toParamType->minValue().toDouble();
double toMax = toParamType->maxValue().toDouble();
double fromValue = value.toDouble();
double fromPercent = (fromValue - fromMin) / (fromMax - fromMin);
double toValue = toMin + (toMax - toMin) * fromPercent;
return toValue;
}

View File

@ -37,6 +37,7 @@
class DevicesProxy; class DevicesProxy;
class DeviceManager; class DeviceManager;
class ParamType;
class ThingGroup : public Device class ThingGroup : public Device
{ {
@ -49,15 +50,13 @@ public:
private: private:
void syncStates(); void syncStates();
signals: QVariant mapValue(const QVariant &value, ParamType *fromParamType, ParamType *toParamType) const;
void actionExecutionFinished(int id, const QString &status);
private: private:
DeviceManager* m_thingManager = nullptr;
DevicesProxy* m_things = nullptr; DevicesProxy* m_things = nullptr;
int m_idCounter = 0; int m_idCounter = 0;
QHash<int, QList<int>> m_pendingActions; QHash<int, QList<int>> m_pendingGroupActions;
}; };
#endif // THINGGROUP_H #endif // THINGGROUP_H

View File

@ -40,6 +40,12 @@ Device::Device(DeviceManager *thingManager, DeviceClass *thingClass, const QUuid
m_parentId(parentId), m_parentId(parentId),
m_thingClass(thingClass) m_thingClass(thingClass)
{ {
connect(thingManager, &DeviceManager::executeActionReply, this, [=](int commandId, const QVariantMap &params){
if (m_pendingActions.contains(commandId)) {
m_pendingActions.removeAll(commandId);
emit executeActionReply(commandId, params);
}
});
} }
QString Device::name() const QString Device::name() const
@ -229,7 +235,9 @@ int Device::executeAction(const QString &actionName, const QVariantList &params)
} }
finalParams.append(param); finalParams.append(param);
} }
return m_thingManager->executeAction(m_id, actionType->id(), finalParams); int commandId = m_thingManager->executeAction(m_id, actionType->id(), finalParams);
m_pendingActions.append(commandId);
return commandId;
} }
QDebug operator<<(QDebug &dbg, Device *thing) QDebug operator<<(QDebug &dbg, Device *thing)

View File

@ -114,6 +114,9 @@ signals:
void statesChanged(); void statesChanged();
void eventTriggered(const QUuid &eventTypeId, const QVariantMap &params); void eventTriggered(const QUuid &eventTypeId, const QVariantMap &params);
signals:
void executeActionReply(int commandId, const QVariantMap &params);
protected: protected:
DeviceManager *m_thingManager = nullptr; DeviceManager *m_thingManager = nullptr;
QString m_name; QString m_name;
@ -125,6 +128,8 @@ protected:
Params *m_settings = nullptr; Params *m_settings = nullptr;
States *m_states = nullptr; States *m_states = nullptr;
DeviceClass *m_thingClass = nullptr; DeviceClass *m_thingClass = nullptr;
QList<int> m_pendingActions;
}; };
QDebug operator<<(QDebug &dbg, Device* thing); QDebug operator<<(QDebug &dbg, Device* thing);

View File

@ -417,7 +417,7 @@ void Interfaces::addStateType(const QString &interfaceName, const QString &name,
st->setMinValue(min); st->setMinValue(min);
st->setMaxValue(max); st->setMaxValue(max);
iface->stateTypes()->addStateType(st); iface->stateTypes()->addStateType(st);
ParamTypes *pts = createParamTypes(name, displayName, type); ParamTypes *pts = createParamTypes(name, displayName, type, QVariant(), min, max);
addEventType(interfaceName, name, displayNameEvent, pts); addEventType(interfaceName, name, displayNameEvent, pts);
if (writable) { if (writable) {
addActionType(interfaceName, name, displayNameAction, pts); addActionType(interfaceName, name, displayNameAction, pts);

View File

@ -200,11 +200,10 @@ MainPageTile {
onClicked: { onClicked: {
switch (iface.name) { switch (iface.name) {
case "light": case "light":
var group = engine.deviceManager.createGroup(Interfaces.findByName("colorlight"), devicesProxy); var group = engine.thingManager.createGroup(Interfaces.findByName("colorlight"), devicesProxy);
print("opening lights page for group", group) print("opening lights page for group", group)
pageStack.push("../devicepages/LightDevicePage.qml", {device: group}) pageStack.push("../devicepages/LightDevicePage.qml", {thing: group})
} }
} }
} }
} }

View File

@ -118,16 +118,16 @@ DevicePageBase {
var absoluteCtValue = (model.ct * (root.ctStateType.maxValue - root.ctStateType.minValue) / 100) + root.ctStateType.minValue var absoluteCtValue = (model.ct * (root.ctStateType.maxValue - root.ctStateType.minValue) / 100) + root.ctStateType.minValue
var params = []; var params = [];
var param1 = {}; var param1 = {};
param1["paramTypeId"] = root.ctActionType.paramTypes.get(0).id; param1["paramName"] = root.ctActionType.paramTypes.get(0).name;
param1["value"] = absoluteCtValue; param1["value"] = absoluteCtValue;
params.push(param1) params.push(param1)
engine.deviceManager.executeAction(root.device.id, root.ctActionType.id, params) root.thing.executeAction(root.ctActionType.name, params)
params = []; params = [];
param1 = {}; param1 = {};
param1["paramTypeId"] = root.brightnessActionType.paramTypes.get(0).id; param1["paramName"] = root.brightnessActionType.paramTypes.get(0).name;
param1["value"] = model.bri; param1["value"] = model.bri;
params.push(param1) params.push(param1)
engine.deviceManager.executeAction(root.device.id, root.brightnessActionType.id, params) root.thing.executeAction(root.brightnessActionType.name, params)
} }
} }
} }
@ -176,7 +176,7 @@ DevicePageBase {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: {
engine.thingManager.executeAction(root.thing.id, root.powerStateType.id, [{paramTypeId: root.powerStateType.id, value: !root.powerState.value}]) root.thing.executeAction("power", [{paramName: "power", value: !root.powerState.value}])
} }
} }
} }

View File

@ -16,10 +16,9 @@ Item {
return; return;
} }
d.pendingValue = value; d.pendingValue = value;
d.pendingCommand = engine.thingManager.executeAction(root.thing.id, d.pendingCommand = root.thing.executeAction(root.stateType.name,
root.stateType.id,
[{ [{
paramTypeId: root.stateType.id, paramName: root.stateType.name,
value: value value: value
}]) }])
d.queuedValue = null d.queuedValue = null
@ -33,7 +32,7 @@ Item {
} }
Connections { Connections {
target: engine.thingManager target: root.thing
onExecuteActionReply: { onExecuteActionReply: {
if (d.pendingCommand == commandId) { if (d.pendingCommand == commandId) {
d.pendingCommand = -1; d.pendingCommand = -1;