add support for interface based rules

pull/135/head
Michael Zanetti 2017-10-18 19:23:04 +02:00
parent 57d4466f9a
commit 58c83e9072
39 changed files with 892 additions and 281 deletions

View File

@ -6,7 +6,7 @@ GUH_PLUGINS_PATH=/usr/lib/$$system('dpkg-architecture -q DEB_HOST_MULTIARCH')/gu
# define protocol versions
JSON_PROTOCOL_VERSION_MAJOR=1
JSON_PROTOCOL_VERSION_MINOR=0
JSON_PROTOCOL_VERSION_MINOR=1
REST_API_VERSION=1
DEFINES += GUH_VERSION_STRING=\\\"$${GUH_VERSION_STRING}\\\" \

View File

@ -338,9 +338,36 @@ DeviceManager::DeviceError GuhCore::executeAction(const Action &action)
/*! Execute the given \a ruleActions. */
void GuhCore::executeRuleActions(const QList<RuleAction> ruleActions)
{
QList<Action> actions;
foreach (const RuleAction &ruleAction, ruleActions) {
Action action = ruleAction.toAction();
qCDebug(dcRuleEngine) << "Executing action" << ruleAction.actionTypeId() << action.params();
if (ruleAction.type() == RuleAction::TypeDevice) {
actions.append(ruleAction.toAction());
} else {
QList<Device*> devices = m_deviceManager->findConfiguredDevices(ruleAction.interface());
foreach (Device* device, devices) {
DeviceClass dc = m_deviceManager->findDeviceClass(device->deviceClassId());
ActionType at = dc.actionTypes().findByName(ruleAction.interfaceAction());
if (at.id().isNull()) {
qCWarning(dcRuleEngine()) << "Error creating Action. The given DeviceClass does not implement action:" << ruleAction.interfaceAction();
continue;
}
Action action = Action(at.id(), device->id());
ParamList params;
foreach (const RuleActionParam &rap, ruleAction.ruleActionParams()) {
ParamType pt = at.paramTypes().findByName(rap.paramName());
if (pt.id().isNull()) {
qCWarning(dcRuleEngine()) << "Error creating Action. Failed to match interface param type to DeviceClass paramtype.";
continue;
}
params.append(Param(pt.id(), rap.value()));
}
action.setParams(params);
actions.append(action);
}
}
}
foreach (const Action &action, actions) {
qCDebug(dcRuleEngine) << "Executing action" << action.actionTypeId() << action.params();
DeviceManager::DeviceError status = executeAction(action);
switch(status) {
case DeviceManager::DeviceErrorNoError:

View File

@ -24,8 +24,8 @@
#include "rule.h"
#include "types/event.h"
#include "types/deviceclass.h"
#include "plugin/deviceplugin.h"
#include "plugin/deviceclass.h"
#include "plugin/devicedescriptor.h"
#include "logging/logengine.h"

View File

@ -57,8 +57,8 @@
#include "guhcore.h"
#include "devicemanager.h"
#include "loggingcategories.h"
#include "types/deviceclass.h"
#include "plugin/device.h"
#include "plugin/deviceclass.h"
#include "plugin/deviceplugin.h"
#include <QDebug>

View File

@ -42,7 +42,7 @@
#include "guhcore.h"
#include "devicemanager.h"
#include "plugin/deviceplugin.h"
#include "plugin/deviceclass.h"
#include "types/deviceclass.h"
#include "plugin/device.h"
#include "rule.h"
#include "ruleengine.h"

View File

@ -22,11 +22,11 @@
#ifndef JSONRPCSERVER_H
#define JSONRPCSERVER_H
#include "plugin/deviceclass.h"
#include "jsonhandler.h"
#include "transportinterface.h"
#include "usermanager.h"
#include "types/deviceclass.h"
#include "types/action.h"
#include "types/event.h"

View File

@ -168,12 +168,15 @@ void JsonTypes::init()
s_param.insert("value", basicTypeRef());
// RuleAction
s_ruleAction.insert("actionTypeId", basicTypeToString(Uuid));
s_ruleAction.insert("deviceId", basicTypeToString(Uuid));
s_ruleAction.insert("o:deviceId", basicTypeToString(Uuid));
s_ruleAction.insert("o:actionTypeId", basicTypeToString(Uuid));
s_ruleAction.insert("o:interface", basicTypeToString(String));
s_ruleAction.insert("o:interfaceAction", basicTypeToString(String));
s_ruleAction.insert("o:ruleActionParams", QVariantList() << ruleActionParamRef());
// RuleActionParam
s_ruleActionParam.insert("paramTypeId", basicTypeToString(Uuid));
s_ruleActionParam.insert("o:paramTypeId", basicTypeToString(Uuid));
s_ruleActionParam.insert("o:paramName", basicTypeToString(String));
s_ruleActionParam.insert("o:value", basicTypeRef());
s_ruleActionParam.insert("o:eventTypeId", basicTypeToString(Uuid));
s_ruleActionParam.insert("o:eventParamTypeId", basicTypeToString(Uuid));
@ -228,8 +231,10 @@ void JsonTypes::init()
s_event.insert("o:params", QVariantList() << paramRef());
// EventDescriptor
s_eventDescriptor.insert("eventTypeId", basicTypeToString(Uuid));
s_eventDescriptor.insert("deviceId", basicTypeToString(Uuid));
s_eventDescriptor.insert("o:eventTypeId", basicTypeToString(Uuid));
s_eventDescriptor.insert("o:deviceId", basicTypeToString(Uuid));
s_eventDescriptor.insert("o:interface", basicTypeToString(String));
s_eventDescriptor.insert("o:interfaceEvent", basicTypeToString(String));
s_eventDescriptor.insert("o:paramDescriptors", QVariantList() << paramDescriptorRef());
// ActionType
@ -1320,10 +1325,16 @@ Rule JsonTypes::unpackRule(const QVariantMap &ruleMap)
/*! Returns a \l{RuleAction} created from the given \a ruleActionMap. */
RuleAction JsonTypes::unpackRuleAction(const QVariantMap &ruleActionMap)
{
RuleAction action(ActionTypeId(ruleActionMap.value("actionTypeId").toString()), DeviceId(ruleActionMap.value("deviceId").toString()));
ActionTypeId actionTypeId(ruleActionMap.value("actionTypeId").toString());
DeviceId actionDeviceId(ruleActionMap.value("deviceId").toString());
QString interface = ruleActionMap.value("interface").toString();
QString interfaceAction = ruleActionMap.value("interfaceAction").toString();
RuleActionParamList actionParamList = JsonTypes::unpackRuleActionParams(ruleActionMap.value("ruleActionParams").toList());
action.setRuleActionParams(actionParamList);
return action;
if (!actionTypeId.isNull() && !actionDeviceId.isNull()) {
return RuleAction(actionTypeId, actionDeviceId, actionParamList);
}
return RuleAction(interface, interfaceAction, actionParamList);
}
/*! Returns a \l{RuleActionParam} created from the given \a ruleActionParamMap. */
@ -1333,9 +1344,13 @@ RuleActionParam JsonTypes::unpackRuleActionParam(const QVariantMap &ruleActionPa
return RuleActionParam();
ParamTypeId paramTypeId = ParamTypeId(ruleActionParamMap.value("paramTypeId").toString());
QString paramName = ruleActionParamMap.value("paramName").toString();
QVariant value = ruleActionParamMap.value("value");
EventTypeId eventTypeId = EventTypeId(ruleActionParamMap.value("eventTypeId").toString());
ParamTypeId eventParamTypeId = ParamTypeId(ruleActionParamMap.value("eventParamTypeId").toString());
if (paramTypeId.isNull()) {
return RuleActionParam(paramName, value, eventTypeId, eventParamTypeId);
}
return RuleActionParam(paramTypeId, value, eventTypeId, eventParamTypeId);
}
@ -1377,9 +1392,13 @@ EventDescriptor JsonTypes::unpackEventDescriptor(const QVariantMap &eventDescrip
{
EventTypeId eventTypeId(eventDescriptorMap.value("eventTypeId").toString());
DeviceId eventDeviceId(eventDescriptorMap.value("deviceId").toString());
QString interface = eventDescriptorMap.value("interface").toString();
QString interfaceEvent = eventDescriptorMap.value("interfaceEvent").toString();
QList<ParamDescriptor> eventParams = JsonTypes::unpackParamDescriptors(eventDescriptorMap.value("paramDescriptors").toList());
EventDescriptor eventDescriptor(eventTypeId, eventDeviceId, eventParams);
return eventDescriptor;
if (!eventDeviceId.isNull() && !eventTypeId.isNull()) {
return EventDescriptor(eventTypeId, eventDeviceId, eventParams);
}
return EventDescriptor(interface, interfaceEvent, eventParams);
}
/*! Returns a \l{StateEvaluator} created from the given \a stateEvaluatorMap. */

View File

@ -23,7 +23,6 @@
#ifndef JSONTYPES_H
#define JSONTYPES_H
#include "plugin/deviceclass.h"
#include "plugin/devicedescriptor.h"
#include "rule.h"
#include "devicemanager.h"
@ -31,6 +30,7 @@
#include "guhconfiguration.h"
#include "usermanager.h"
#include "types/deviceclass.h"
#include "types/event.h"
#include "types/action.h"
#include "types/actiontype.h"

View File

@ -369,7 +369,7 @@ QList<Rule> RuleEngine::evaluateEvent(const Event &event)
}
} else {
// Event based rule
if (containsEvent(rule, event) && rule.statesActive() && rule.timeActive()) {
if (containsEvent(rule, event, device->deviceClassId()) && rule.statesActive() && rule.timeActive()) {
qCDebug(dcRuleEngine) << "Rule" << rule.id() << "contains event" << event.eventId() << "and all states match.";
rules.append(rule);
}
@ -465,25 +465,42 @@ RuleEngine::RuleError RuleEngine::addRule(const Rule &rule, bool fromEdit)
// Check IDs in each EventDescriptor
foreach (const EventDescriptor &eventDescriptor, rule.eventDescriptors()) {
// check deviceId
Device *device = GuhCore::instance()->deviceManager()->findConfiguredDevice(eventDescriptor.deviceId());
if (!device) {
qCWarning(dcRuleEngine) << "Cannot create rule. No configured device for eventTypeId" << eventDescriptor.eventTypeId();
return RuleErrorDeviceNotFound;
}
// Check eventTypeId for this deivce
DeviceClass deviceClass = GuhCore::instance()->deviceManager()->findDeviceClass(device->deviceClassId());
bool eventTypeFound = false;
foreach (const EventType &eventType, deviceClass.eventTypes()) {
if (eventType.id() == eventDescriptor.eventTypeId()) {
eventTypeFound = true;
}
}
if (!eventTypeFound) {
qCWarning(dcRuleEngine) << "Cannot create rule. Device " + device->name() + " has no event type:" << eventDescriptor.eventTypeId();
if (!eventDescriptor.isValid()) {
qWarning(dcRuleEngine()) << "EventDescriptor is incomplete. It must have either eventTypeId and deviceId, or interface and interfaceEvent";
return RuleErrorEventTypeNotFound;
}
if (eventDescriptor.type() == EventDescriptor::TypeDevice) {
// check deviceId
Device *device = GuhCore::instance()->deviceManager()->findConfiguredDevice(eventDescriptor.deviceId());
if (!device) {
qCWarning(dcRuleEngine) << "Cannot create rule. No configured device for eventTypeId" << eventDescriptor.eventTypeId();
return RuleErrorDeviceNotFound;
}
// Check eventTypeId for this deivce
DeviceClass deviceClass = GuhCore::instance()->deviceManager()->findDeviceClass(device->deviceClassId());
bool eventTypeFound = false;
foreach (const EventType &eventType, deviceClass.eventTypes()) {
if (eventType.id() == eventDescriptor.eventTypeId()) {
eventTypeFound = true;
}
}
if (!eventTypeFound) {
qCWarning(dcRuleEngine) << "Cannot create rule. Device " + device->name() + " has no event type:" << eventDescriptor.eventTypeId();
return RuleErrorEventTypeNotFound;
}
} else {
// Interface based event
Interface iface = GuhCore::instance()->deviceManager()->supportedInterfaces().findByName(eventDescriptor.interface());
if (!iface.isValid()) {
qWarning(dcRuleEngine()) << "No such interface:" << eventDescriptor.interface();
return RuleErrorInterfaceNotFound;
}
if (iface.eventTypes().findByName(eventDescriptor.interfaceEvent()).name().isEmpty()) {
qWarning(dcRuleEngine()) << "Interface" << iface.name() << "has no such event:" << eventDescriptor.interfaceEvent();
return RuleErrorEventTypeNotFound;
}
}
}
// Check state evaluator
@ -536,104 +553,165 @@ RuleEngine::RuleError RuleEngine::addRule(const Rule &rule, bool fromEdit)
// Check actions
foreach (const RuleAction &action, rule.actions()) {
Device *device = GuhCore::instance()->deviceManager()->findConfiguredDevice(action.deviceId());
if (!device) {
qCWarning(dcRuleEngine) << "Cannot create rule. No configured device for action with actionTypeId" << action.actionTypeId();
return RuleErrorDeviceNotFound;
}
DeviceClass deviceClass = GuhCore::instance()->deviceManager()->findDeviceClass(device->deviceClassId());
if (!deviceClass.hasActionType(action.actionTypeId())) {
qCWarning(dcRuleEngine) << "Cannot create rule. Device " + device->name() + " has no action type:" << action.actionTypeId();
if (!action.isValid()) {
qWarning(dcRuleEngine()) << "Action is incomplete. It must have either actionTypeId and deviceId, or interface and interfaceAction";
return RuleErrorActionTypeNotFound;
}
if (action.type() == RuleAction::TypeDevice) {
Device *device = GuhCore::instance()->deviceManager()->findConfiguredDevice(action.deviceId());
if (!device) {
qCWarning(dcRuleEngine) << "Cannot create rule. No configured device for action with actionTypeId" << action.actionTypeId();
return RuleErrorDeviceNotFound;
}
DeviceClass deviceClass = GuhCore::instance()->deviceManager()->findDeviceClass(device->deviceClassId());
if (!deviceClass.hasActionType(action.actionTypeId())) {
qCWarning(dcRuleEngine) << "Cannot create rule. Device " + device->name() + " has no action type:" << action.actionTypeId();
return RuleErrorActionTypeNotFound;
}
// check possible eventTypeIds in params
if (action.isEventBased()) {
foreach (const RuleActionParam &ruleActionParam, action.ruleActionParams()) {
if (ruleActionParam.eventTypeId() != EventTypeId()) {
// We have an eventTypeId
if (rule.eventDescriptors().isEmpty()) {
qCWarning(dcRuleEngine) << "Cannot create rule. RuleAction" << action.actionTypeId() << "contains an eventTypeId, but there are no eventDescriptors.";
return RuleErrorInvalidRuleActionParameter;
}
// now check if this eventType is in the eventDescriptorList of this rule
if (!checkEventDescriptors(rule.eventDescriptors(), ruleActionParam.eventTypeId())) {
qCWarning(dcRuleEngine) << "Cannot create rule. EventTypeId from RuleAction" << action.actionTypeId() << "not in eventDescriptors.";
return RuleErrorInvalidRuleActionParameter;
}
// check if the param type of the event and the action match
QVariant::Type eventParamType = getEventParamType(ruleActionParam.eventTypeId(), ruleActionParam.eventParamTypeId());
QVariant::Type actionParamType = getActionParamType(action.actionTypeId(), ruleActionParam.paramTypeId());
if (eventParamType != actionParamType) {
qCWarning(dcRuleEngine) << "Cannot create rule. RuleActionParam" << ruleActionParam.paramTypeId().toString() << " and given event param " << ruleActionParam.eventParamTypeId().toString() << "have not the same type:";
qCWarning(dcRuleEngine) << " -> actionParamType:" << actionParamType;
qCWarning(dcRuleEngine) << " -> eventParamType:" << eventParamType;
return RuleErrorTypesNotMatching;
}
}
}
} else {
// verify action params
foreach (const ActionType &actionType, deviceClass.actionTypes()) {
if (actionType.id() == action.actionTypeId()) {
ParamList finalParams = action.toAction().params();
DeviceManager::DeviceError paramCheck = GuhCore::instance()->deviceManager()->verifyParams(actionType.paramTypes(), finalParams);
if (paramCheck != DeviceManager::DeviceErrorNoError) {
qCWarning(dcRuleEngine) << "Cannot create rule. Got an invalid actionParam.";
return RuleErrorInvalidRuleActionParameter;
}
}
}
}
// check possible eventTypeIds in params
if (action.isEventBased()) {
foreach (const RuleActionParam &ruleActionParam, action.ruleActionParams()) {
if (ruleActionParam.eventTypeId() != EventTypeId()) {
// We have an eventTypeId
if (rule.eventDescriptors().isEmpty()) {
qCWarning(dcRuleEngine) << "Cannot create rule. RuleAction" << action.actionTypeId() << "contains an eventTypeId, but there are no eventDescriptors.";
return RuleErrorInvalidRuleActionParameter;
}
// now check if this eventType is in the eventDescriptorList of this rule
if (!checkEventDescriptors(rule.eventDescriptors(), ruleActionParam.eventTypeId())) {
qCWarning(dcRuleEngine) << "Cannot create rule. EventTypeId from RuleAction" << action.actionTypeId() << "not in eventDescriptors.";
return RuleErrorInvalidRuleActionParameter;
}
// check if the param type of the event and the action match
QVariant::Type eventParamType = getEventParamType(ruleActionParam.eventTypeId(), ruleActionParam.eventParamTypeId());
QVariant::Type actionParamType = getActionParamType(action.actionTypeId(), ruleActionParam.paramTypeId());
if (eventParamType != actionParamType) {
qCWarning(dcRuleEngine) << "Cannot create rule. RuleActionParam" << ruleActionParam.paramTypeId().toString() << " and given event param " << ruleActionParam.eventParamTypeId().toString() << "have not the same type:";
qCWarning(dcRuleEngine) << " -> actionParamType:" << actionParamType;
qCWarning(dcRuleEngine) << " -> eventParamType:" << eventParamType;
return RuleErrorTypesNotMatching;
}
if (!ruleActionParam.isValid()) {
qCWarning(dcRuleEngine) << "Cannot create rule. Got an actionParam with \"value\" AND \"eventTypeId\".";
return RuleEngine::RuleErrorInvalidRuleActionParameter;
}
}
} else {
// verify action params
foreach (const ActionType &actionType, deviceClass.actionTypes()) {
if (actionType.id() == action.actionTypeId()) {
ParamList finalParams = action.toAction().params();
DeviceManager::DeviceError paramCheck = GuhCore::instance()->deviceManager()->verifyParams(actionType.paramTypes(), finalParams);
if (paramCheck != DeviceManager::DeviceErrorNoError) {
qCWarning(dcRuleEngine) << "Cannot create rule. Got an invalid actionParam.";
return RuleErrorInvalidRuleActionParameter;
}
} else { // Is TypeInterface
Interface iface = GuhCore::instance()->deviceManager()->supportedInterfaces().findByName(action.interface());
if (!iface.isValid()) {
qCWarning(dcRuleEngine()) << "Cannot create rule. No such interface:" << action.interface();
return RuleError::RuleErrorInterfaceNotFound;
}
ActionType ifaceActionType = iface.actionTypes().findByName(action.interfaceAction());
if (ifaceActionType.name().isEmpty()) {
qCWarning(dcRuleEngine()) << "Cannot create rule. Interface" << iface.name() << "does not implement action" << action.interfaceAction();
return RuleError::RuleErrorActionTypeNotFound;
}
foreach (const ParamType &ifaceActionParamType, ifaceActionType.paramTypes()) {
qWarning() << "iface requires param:" << ifaceActionParamType.name();
if (!action.ruleActionParams().hasParam(ifaceActionParamType.name())) {
qCWarning(dcRuleEngine()) << "Cannot create rule. Interface action" << iface.name() << ":" << action.interfaceAction() << "requires a" << ifaceActionParamType.name() << "param of type" << QVariant::typeToName(ifaceActionParamType.type());
return RuleError::RuleErrorMissingParameter;
}
if (!action.ruleActionParam(ifaceActionParamType.name()).value().canConvert(ifaceActionParamType.type())) {
qCWarning(dcRuleEngine()) << "Cannot create rule. Interface action parameter" << iface.name() << ":" << action.interfaceAction() << ":" << ifaceActionParamType.name() << "has wrong type. Expected" << QVariant::typeToName(ifaceActionParamType.type());
return RuleError::RuleErrorInvalidParameter;
}
}
}
foreach (const RuleActionParam &ruleActionParam, action.ruleActionParams()) {
if (!ruleActionParam.isValid()) {
qCWarning(dcRuleEngine) << "Cannot create rule. Got an actionParam with \"value\" AND \"eventTypeId\".";
return RuleEngine::RuleErrorInvalidRuleActionParameter;
}
// TODO: Check params
}
}
// Check exit actions
foreach (const RuleAction &action, rule.exitActions()) {
Device *device = GuhCore::instance()->deviceManager()->findConfiguredDevice(action.deviceId());
if (!device) {
qCWarning(dcRuleEngine) << "Cannot create rule. No configured device for exit action with actionTypeId" << action.actionTypeId();
return RuleErrorDeviceNotFound;
}
DeviceClass deviceClass = GuhCore::instance()->deviceManager()->findDeviceClass(device->deviceClassId());
if (!deviceClass.hasActionType(action.actionTypeId())) {
qCWarning(dcRuleEngine) << "Cannot create rule. Device " + device->name() + " has no action type:" << action.actionTypeId();
foreach (const RuleAction &ruleAction, rule.exitActions()) {
if (!ruleAction.isValid()) {
qWarning(dcRuleEngine()) << "Exit Action is incomplete. It must have either actionTypeId and deviceId, or interface and interfaceAction";
return RuleErrorActionTypeNotFound;
}
// verify action params
foreach (const ActionType &actionType, deviceClass.actionTypes()) {
if (actionType.id() == action.actionTypeId()) {
ParamList finalParams = action.toAction().params();
DeviceManager::DeviceError paramCheck = GuhCore::instance()->deviceManager()->verifyParams(actionType.paramTypes(), finalParams);
if (paramCheck != DeviceManager::DeviceErrorNoError) {
qCWarning(dcRuleEngine) << "Cannot create rule. Got an invalid exit actionParam.";
return RuleErrorInvalidRuleActionParameter;
if (ruleAction.type() == RuleAction::TypeDevice) {
Device *device = GuhCore::instance()->deviceManager()->findConfiguredDevice(ruleAction.deviceId());
if (!device) {
qCWarning(dcRuleEngine) << "Cannot create rule. No configured device for exit action with actionTypeId" << ruleAction.actionTypeId();
return RuleErrorDeviceNotFound;
}
DeviceClass deviceClass = GuhCore::instance()->deviceManager()->findDeviceClass(device->deviceClassId());
if (!deviceClass.hasActionType(ruleAction.actionTypeId())) {
qCWarning(dcRuleEngine) << "Cannot create rule. Device " + device->name() + " has no action type:" << ruleAction.actionTypeId();
return RuleErrorActionTypeNotFound;
}
// verify action params
foreach (const ActionType &actionType, deviceClass.actionTypes()) {
if (actionType.id() == ruleAction.actionTypeId()) {
ParamList finalParams = ruleAction.toAction().params();
DeviceManager::DeviceError paramCheck = GuhCore::instance()->deviceManager()->verifyParams(actionType.paramTypes(), finalParams);
if (paramCheck != DeviceManager::DeviceErrorNoError) {
qCWarning(dcRuleEngine) << "Cannot create rule. Got an invalid exit actionParam.";
return RuleErrorInvalidRuleActionParameter;
}
}
}
}
// Exit action can never be event based.
if (action.isEventBased()) {
qCWarning(dcRuleEngine) << "Cannot create rule. Got exitAction with an actionParam containing an eventTypeId. ";
return RuleErrorInvalidRuleActionParameter;
}
foreach (const RuleActionParam &ruleActionParam, action.ruleActionParams()) {
if (!ruleActionParam.isValid()) {
qCWarning(dcRuleEngine) << "Cannot create rule. Got an actionParam with \"value\" AND \"eventTypeId\".";
return RuleEngine::RuleErrorInvalidRuleActionParameter;
// Exit action can never be event based.
if (ruleAction.isEventBased()) {
qCWarning(dcRuleEngine) << "Cannot create rule. Got exitAction with an actionParam containing an eventTypeId. ";
return RuleErrorInvalidRuleActionParameter;
}
foreach (const RuleActionParam &ruleActionParam, ruleAction.ruleActionParams()) {
if (!ruleActionParam.isValid()) {
qCWarning(dcRuleEngine) << "Cannot create rule. Got an actionParam with \"value\" AND \"eventTypeId\".";
return RuleEngine::RuleErrorInvalidRuleActionParameter;
}
}
} else { // Is TypeInterface
Interface iface = GuhCore::instance()->deviceManager()->supportedInterfaces().findByName(ruleAction.interface());
if (!iface.isValid()) {
qCWarning(dcRuleEngine()) << "Cannot create rule. No such interface:" << ruleAction.interface();
return RuleError::RuleErrorInterfaceNotFound;
}
ActionType ifaceActionType = iface.actionTypes().findByName(ruleAction.interfaceAction());
if (ifaceActionType.name().isEmpty()) {
qCWarning(dcRuleEngine()) << "Cannot create rule. Interface" << iface.name() << "does not implement action" << ruleAction.interfaceAction();
return RuleError::RuleErrorActionTypeNotFound;
}
foreach (const ParamType &ifaceActionParamType, ifaceActionType.paramTypes()) {
qWarning() << "iface requires param:" << ifaceActionParamType.name();
if (!ruleAction.ruleActionParams().hasParam(ifaceActionParamType.name())) {
qCWarning(dcRuleEngine()) << "Cannot create rule. Interface action" << iface.name() << ":" << ruleAction.interfaceAction() << "requires a" << ifaceActionParamType.name() << "param of type" << QVariant::typeToName(ifaceActionParamType.type());
return RuleError::RuleErrorMissingParameter;
}
if (!ruleAction.ruleActionParam(ifaceActionParamType.name()).value().canConvert(ifaceActionParamType.type())) {
qCWarning(dcRuleEngine()) << "Cannot create rule. Interface action parameter" << iface.name() << ":" << ruleAction.interfaceAction() << ":" << ifaceActionParamType.name() << "has wrong type. Expected" << QVariant::typeToName(ifaceActionParamType.type());
return RuleError::RuleErrorInvalidParameter;
}
}
// TODO: Check params
}
}
@ -997,12 +1075,68 @@ void RuleEngine::removeDeviceFromRule(const RuleId &id, const DeviceId &deviceId
emit ruleConfigurationChanged(newRule);
}
bool RuleEngine::containsEvent(const Rule &rule, const Event &event)
bool RuleEngine::containsEvent(const Rule &rule, const Event &event, const DeviceClassId &deviceClassId)
{
foreach (const EventDescriptor &eventDescriptor, rule.eventDescriptors()) {
if (eventDescriptor == event) {
return true;
// If this is a device based rule, eventTypeId and deviceId must match
if (eventDescriptor.type() == EventDescriptor::TypeDevice) {
if (eventDescriptor.eventTypeId() != event.eventTypeId() || eventDescriptor.deviceId() != event.deviceId()) {
continue;
}
}
// If this is a interface based rule, the device must implement the interface
if (eventDescriptor.type() == EventDescriptor::TypeInterface) {
DeviceClass dc = GuhCore::instance()->deviceManager()->findDeviceClass(deviceClassId);
if (!dc.interfaces().contains(eventDescriptor.interface())) {
// DeviceClass for this event doesn't implement the interface for this eventDescriptor
continue;
}
EventType et = dc.eventTypes().findById(event.eventTypeId());
if (et.name() != eventDescriptor.interfaceEvent()) {
// The fired event name does not match with the eventDescriptor's interfaceEvent
continue;
}
}
// Ok, either device/eventTypeId or interface/interfaceEvent are matching. Compare the paramdescriptor
foreach (const ParamDescriptor &paramDescriptor, eventDescriptor.paramDescriptors()) {
switch (paramDescriptor.operatorType()) {
case Types::ValueOperatorEquals:
if (event.param(paramDescriptor.paramTypeId()).value() != paramDescriptor.value()) {
continue;
}
break;
case Types::ValueOperatorNotEquals:
if (event.param(paramDescriptor.paramTypeId()).value() == paramDescriptor.value()) {
continue;
}
break;
case Types::ValueOperatorGreater:
if (event.param(paramDescriptor.paramTypeId()).value() <= paramDescriptor.value()) {
continue;
}
break;
case Types::ValueOperatorGreaterOrEqual:
if (event.param(paramDescriptor.paramTypeId()).value() < paramDescriptor.value()) {
continue;
}
break;
case Types::ValueOperatorLess:
if (event.param(paramDescriptor.paramTypeId()).value() >= paramDescriptor.value()) {
continue;
}
break;
case Types::ValueOperatorLessOrEqual:
if (event.param(paramDescriptor.paramTypeId()).value() < paramDescriptor.value()) {
continue;
}
break;
}
}
// All matching!
return true;
}
return false;

View File

@ -24,7 +24,7 @@
#include "rule.h"
#include "types/event.h"
#include "plugin/deviceclass.h"
#include "types/deviceclass.h"
#include "stateevaluator.h"
#include <QObject>
@ -59,7 +59,8 @@ public:
RuleErrorInvalidCalendarItem,
RuleErrorInvalidTimeEventItem,
RuleErrorContainsEventBasesAction,
RuleErrorNoExitActions
RuleErrorNoExitActions,
RuleErrorInterfaceNotFound
};
enum RemovePolicy {
@ -99,7 +100,7 @@ signals:
void ruleConfigurationChanged(const Rule &rule);
private:
bool containsEvent(const Rule &rule, const Event &event);
bool containsEvent(const Rule &rule, const Event &event, const DeviceClassId &deviceClassId);
bool containsState(const StateEvaluator &stateEvaluator, const Event &stateChangeEvent);
bool checkEventDescriptors(const QList<EventDescriptor> eventDescriptors, const EventTypeId &eventTypeId);

View File

@ -187,6 +187,10 @@ DeviceManager::DeviceManager(HardwareManager *hardwareManager, const QLocale &lo
qRegisterMetaType<DeviceClassId>();
qRegisterMetaType<DeviceDescriptor>();
foreach (const Interface &interface, DevicePlugin::allInterfaces()) {
m_supportedInterfaces.insert(interface.name(), interface);
}
// Give hardware a chance to start up before loading plugins etc.
QMetaObject::invokeMethod(this, "loadPlugins", Qt::QueuedConnection);
QMetaObject::invokeMethod(this, "loadConfiguredDevices", Qt::QueuedConnection);
@ -342,6 +346,18 @@ QList<Vendor> DeviceManager::supportedVendors() const
return m_supportedVendors.values();
}
/*! Returns the list of all supported interfaces */
Interfaces DeviceManager::supportedInterfaces() const
{
return m_supportedInterfaces.values();
}
/*! Returns the interface with the given name. If the interface can't be found it will return an invalid interface. */
Interface DeviceManager::findInterface(const QString &name)
{
return m_supportedInterfaces.value(name);
}
/*! Returns all the supported \l{DeviceClass}{DeviceClasses} by all \l{DevicePlugin}{DevicePlugins} loaded in the system.
* Optionally filtered by \a vendorId. */
QList<DeviceClass> DeviceManager::supportedDevices(const VendorId &vendorId) const
@ -769,6 +785,18 @@ QList<Device *> DeviceManager::findConfiguredDevices(const DeviceClassId &device
return ret;
}
QList<Device *> DeviceManager::findConfiguredDevices(const QString &interface) const
{
QList<Device*> ret;
foreach (Device *device, m_configuredDevices) {
DeviceClass dc = m_supportedDevices.value(device->deviceClassId());
if (dc.interfaces().contains(interface)) {
ret.append(device);
}
}
return ret;
}
/*! Returns all child \l{Device}{Devices} of the given \a device. */
QList<Device *> DeviceManager::findChildDevices(const DeviceId &id) const
{
@ -793,7 +821,7 @@ DeviceClass DeviceManager::findDeviceClass(const DeviceClassId &deviceClassId) c
return DeviceClass();
}
/*! Verify if the given \a params matche the given \a paramTypes. Ith \a requireAll
/*! 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{DeviceError} to inform about the result.*/
DeviceManager::DeviceError DeviceManager::verifyParams(const QList<ParamType> paramTypes, ParamList &params, bool requireAll)
{

View File

@ -26,10 +26,11 @@
#include "libguh.h"
#include "plugin/deviceclass.h"
#include "plugin/device.h"
#include "plugin/devicedescriptor.h"
#include "types/deviceclass.h"
#include "types/interface.h"
#include "types/event.h"
#include "types/action.h"
#include "types/vendor.h"
@ -104,7 +105,10 @@ public:
DeviceError setPluginConfig(const PluginId &pluginId, const ParamList &pluginConfig);
QList<Vendor> supportedVendors() const;
Interfaces supportedInterfaces() const;
Interface findInterface(const QString &name);
QList<DeviceClass> supportedDevices(const VendorId &vendorId = VendorId()) const;
DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList &params);
QList<Device*> configuredDevices() const;
@ -124,6 +128,7 @@ public:
Device* findConfiguredDevice(const DeviceId &id) const;
QList<Device *> findConfiguredDevices(const DeviceClassId &deviceClassId) const;
QList<Device *> findConfiguredDevices(const QString &interface) const;
QList<Device *> findChildDevices(const DeviceId &id) const;
DeviceClass findDeviceClass(const DeviceClassId &deviceClassId) const;
@ -181,6 +186,7 @@ private:
QLocale m_locale;
QHash<VendorId, Vendor> m_supportedVendors;
QHash<QString, Interface> m_supportedInterfaces;
QHash<VendorId, QList<DeviceClassId> > m_vendorDeviceMap;
QHash<DeviceClassId, DeviceClass> m_supportedDevices;
QHash<DeviceId, Device*> m_configuredDevices;

View File

@ -0,0 +1,14 @@
{
"states": [
{
"name": "batteryLevel",
"type": "int",
"minValue": 0,
"maxValue": 100
},
{
"name": "batteryCritical",
"type": "bool"
}
]
}

View File

@ -13,5 +13,6 @@
<file>extendedvolumecontroller.json</file>
<file>mediametadataprovider.json</file>
<file>mediaplayer.json</file>
<file>battery.json</file>
</qresource>
</RCC>

View File

@ -2,8 +2,8 @@
"states": [
{
"name": "power",
"type": "bool"
"type": "bool",
"writable": true
}
]
}

View File

@ -20,7 +20,6 @@ HEADERS += devicemanager.h \
loggingcategories.h \
guhsettings.h \
plugin/device.h \
plugin/deviceclass.h \
plugin/deviceplugin.h \
plugin/devicedescriptor.h \
plugin/devicepairinginfo.h \
@ -53,6 +52,7 @@ HEADERS += devicemanager.h \
coap/corelinkparser.h \
coap/corelink.h \
coap/coapobserveresource.h \
types/deviceclass.h \
types/action.h \
types/actiontype.h \
types/state.h \
@ -67,6 +67,7 @@ HEADERS += devicemanager.h \
types/ruleaction.h \
types/ruleactionparam.h \
types/statedescriptor.h \
types/interface.h \
hardwareresource.h \
plugintimer.h \
hardwaremanager.h \
@ -75,7 +76,6 @@ SOURCES += devicemanager.cpp \
loggingcategories.cpp \
guhsettings.cpp \
plugin/device.cpp \
plugin/deviceclass.cpp \
plugin/deviceplugin.cpp \
plugin/devicedescriptor.cpp \
plugin/devicepairinginfo.cpp \
@ -108,6 +108,7 @@ SOURCES += devicemanager.cpp \
coap/corelinkparser.cpp \
coap/corelink.cpp \
coap/coapobserveresource.cpp \
types/deviceclass.cpp \
types/action.cpp \
types/actiontype.cpp \
types/state.cpp \
@ -122,6 +123,7 @@ SOURCES += devicemanager.cpp \
types/ruleaction.cpp \
types/ruleactionparam.cpp \
types/statedescriptor.cpp \
types/interface.cpp \
hardwareresource.cpp \
plugintimer.cpp \
hardwaremanager.cpp \

View File

@ -27,7 +27,7 @@
#include "typeutils.h"
#include "libguh.h"
#include "plugin/deviceclass.h"
#include "types/deviceclass.h"
#include "types/state.h"
#include "types/param.h"

View File

@ -797,102 +797,89 @@ void DevicePlugin::loadMetaData()
QStringList interfaces;
foreach (const QJsonValue &value, deviceClassObject.value("interfaces").toArray()) {
QVariantMap interfaceMap = loadInterface(value.toString());
QVariantList states = interfaceMap.value("states").toList();
Interface iface = loadInterface(value.toString());
StateTypes stateTypes(deviceClass.stateTypes());
ActionTypes actionTypes(deviceClass.actionTypes());
EventTypes eventTypes(deviceClass.eventTypes());
bool valid = true;
foreach (const QVariant &stateVariant, states) {
StateType stateType = stateTypes.findByName(stateVariant.toMap().value("name").toString());
QVariantMap stateMap = stateVariant.toMap();
foreach (const StateType &ifaceStateType, iface.stateTypes()) {
StateType stateType = stateTypes.findByName(ifaceStateType.name());
if (stateType.id().isNull()) {
qCWarning(dcDeviceManager) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but doesn't implement state" << stateMap.value("name").toString();
qCWarning(dcDeviceManager) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but doesn't implement state" << ifaceStateType.name();
valid = false;
continue;
}
if (QVariant::nameToType(stateMap.value("type").toByteArray().data()) != stateType.type()) {
qCWarning(dcDeviceManager) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but state" << stateMap.value("name").toString() << "has not matching type" << stateMap.value("type").toString();
if (ifaceStateType.type() != stateType.type()) {
qCWarning(dcDeviceManager) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but state" << stateType.name() << "has not matching type" << stateType.type() << "!=" << ifaceStateType.type();
valid = false;
continue;
}
if (stateMap.contains("minimumValue")) {
if (stateMap.value("minimumValue").toString() == "any") {
if (ifaceStateType.minValue().isValid() && !ifaceStateType.minValue().isNull()) {
if (ifaceStateType.minValue().toString() == "any") {
if (stateType.minValue().isNull()) {
qCWarning(dcDeviceManager) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but state" << stateMap.value("name").toString() << "has no minimum value defined.";
qCWarning(dcDeviceManager) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but state" << stateType.name() << "has no minimum value defined.";
valid = false;
continue;
}
} else if (stateMap.value("minimumValue") != stateType.minValue()) {
qCWarning(dcDeviceManager) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but state" << stateMap.value("name").toString() << "has not matching minimum value:" << stateMap.value("minimumValue") << "!=" << stateType.minValue();
} else if (ifaceStateType.minValue() != stateType.minValue()) {
qCWarning(dcDeviceManager) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but state" << stateType.name() << "has not matching minimum value:" << ifaceStateType.minValue() << "!=" << stateType.minValue();
valid = false;
continue;
}
}
if (stateMap.contains("maximumValue")) {
if (stateMap.value("maximumValue").toString() == "any") {
if (ifaceStateType.maxValue().isValid() && !ifaceStateType.maxValue().isNull()) {
if (ifaceStateType.maxValue().toString() == "any") {
if (stateType.maxValue().isNull()) {
qCWarning(dcDeviceManager) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but state" << stateMap.value("name").toString() << "has no maximum value defined.";
qCWarning(dcDeviceManager) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but state" << stateType.name() << "has no maximum value defined.";
valid = false;
continue;
}
} else if (stateMap.value("maximumValue") != stateType.maxValue()) {
qCWarning(dcDeviceManager) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but state" << stateMap.value("name").toString() << "has not matching maximum value:" << stateMap.value("maximumValue") << "!=" << stateType.minValue();
} else if (ifaceStateType.maxValue() != stateType.maxValue()) {
qCWarning(dcDeviceManager) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but state" << stateType.name() << "has not matching maximum value:" << ifaceStateType.maxValue() << "!=" << stateType.minValue();
valid = false;
continue;
}
}
if (stateMap.contains("allowedValues") && stateMap.value("allowedValues") != stateType.possibleValues()) {
qCWarning(dcDeviceManager) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but state" << stateMap.value("name").toString() << "has not matching allowed values" << stateMap.value("allowedValues") << "!=" << stateType.possibleValues();
valid = false;
continue;
}
if (stateMap.contains("writable") && stateMap.value("writable").toBool() && actionTypes.findById(ActionTypeId(stateType.id().toString())).id().isNull()) {
qCWarning(dcDeviceManager) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but state" << stateMap.value("name").toString() << "is not writable while it should be";
if (!ifaceStateType.possibleValues().isEmpty() && ifaceStateType.possibleValues() != stateType.possibleValues()) {
qCWarning(dcDeviceManager) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but state" << stateType.name() << "has not matching allowed values" << ifaceStateType.possibleValues() << "!=" << stateType.possibleValues();
valid = false;
continue;
}
}
QVariantList actions = interfaceMap.value("actions").toList();
foreach (const QVariant &actionVariant, actions) {
QVariantMap actionMap = actionVariant.toMap();
ActionType actionType = actionTypes.findByName(actionMap.value("name").toString());
foreach (const ActionType &ifaceActionType, iface.actionTypes()) {
ActionType actionType = actionTypes.findByName(ifaceActionType.name());
if (actionType.id().isNull()) {
qCWarning(dcDeviceManager) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but doesn't implement action" << actionMap.value("name").toString();
qCWarning(dcDeviceManager) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but doesn't implement action" << ifaceActionType.name();
valid = false;
}
QVariantList params = actionMap.value("params").toList();
foreach (const QVariant &paramVariant, params) {
ParamType paramType = actionType.paramTypes().findByName(paramVariant.toMap().value("name").toString());
foreach (const ParamType &ifaceActionParamType, ifaceActionType.paramTypes()) {
ParamType paramType = actionType.paramTypes().findByName(ifaceActionParamType.name());
if (!paramType.isValid()) {
qCWarning(dcDeviceManager) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but doesn't implement action param" << actionMap.value("name").toString() << ":" << paramVariant.toMap().value("name").toString();
qCWarning(dcDeviceManager) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but doesn't implement action param" << ifaceActionType.name() << ":" << ifaceActionParamType.name();
valid = false;
} else {
if (paramType.type() != QVariant::nameToType(paramVariant.toMap().value("type").toString().toLatin1())) {
qCWarning(dcDeviceManager()) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but param" << paramType.name() << "is of wrong type:" << QVariant::typeToName(paramType.type()) << "expected:" << paramVariant.toMap().value("type").toString();
if (paramType.type() != ifaceActionParamType.type()) {
qCWarning(dcDeviceManager()) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but param" << paramType.name() << "is of wrong type:" << QVariant::typeToName(paramType.type()) << "expected:" << QVariant::typeToName(ifaceActionParamType.type());
valid = false;
}
}
}
}
QVariantList events = interfaceMap.value("events").toList();
foreach (const QVariant &eventVariant, events) {
QVariantMap eventMap = eventVariant.toMap();
EventType eventType = eventTypes.findByName(eventMap.value("name").toString());
if (eventType.isValid()) {
qCWarning(dcDeviceManager) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but doesn't implement event" << eventMap.value("name").toString();
foreach (const EventType &ifaceEventType, iface.eventTypes()) {
EventType eventType = eventTypes.findByName(ifaceEventType.name());
if (!eventType.isValid()) {
qCWarning(dcDeviceManager) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but doesn't implement event" << ifaceEventType.name();
valid = false;
}
QVariantList params = eventMap.value("params").toList();
foreach (const QVariant &paramVariant, params) {
ParamType paramType = eventType.paramTypes().findByName(paramVariant.toMap().value("name").toString());
foreach (const ParamType &ifaceEventParamType, ifaceEventType.paramTypes()) {
ParamType paramType = eventType.paramTypes().findByName(ifaceEventParamType.name());
if (!paramType.isValid()) {
qCWarning(dcDeviceManager) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but doesn't implement action param" << eventMap.value("name").toString() << ":" << paramVariant.toMap().value("name").toString();
qCWarning(dcDeviceManager) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but doesn't implement event param" << ifaceEventType.name() << ":" << ifaceEventParamType.name();
valid = false;
} else {
if (paramType.type() != QVariant::nameToType(paramVariant.toMap().value("type").toString().toLatin1())) {
qCWarning(dcDeviceManager()) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but param" << paramType.name() << "is of wrong type:" << QVariant::typeToName(paramType.type()) << "expected:" << paramVariant.toMap().value("type").toString();
if (paramType.type() != ifaceEventParamType.type()) {
qCWarning(dcDeviceManager()) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but param" << paramType.name() << "is of wrong type:" << QVariant::typeToName(paramType.type()) << "expected:" << QVariant::typeToName(ifaceEventParamType.type());
valid = false;
}
}
@ -1038,36 +1025,100 @@ QPair<bool, DeviceClass::DeviceIcon> DevicePlugin::loadAndVerifyDeviceIcon(const
return QPair<bool, DeviceClass::DeviceIcon>(true, (DeviceClass::DeviceIcon)enumValue);
}
QVariantMap DevicePlugin::loadInterface(const QString &name)
Interfaces DevicePlugin::allInterfaces()
{
Interfaces ret;
QDir dir(":/interfaces/");
foreach (const QFileInfo &ifaceFile, dir.entryInfoList()) {
ret.append(loadInterface(ifaceFile.baseName()));
}
return ret;
}
Interface DevicePlugin::loadInterface(const QString &name)
{
Interface iface;
QFile f(QString(":/interfaces/%1.json").arg(name));
if (!f.open(QFile::ReadOnly)) {
qCWarning(dcDeviceManager()) << "Failed to load interface" << name;
return QVariantMap();
return iface;
}
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(f.readAll(), &error);
if (error.error != QJsonParseError::NoError) {
qCWarning(dcDeviceManager) << "Cannot load interface definition for interface" << name << ":" << error.errorString();
return QVariantMap();
return iface;
}
QVariantMap content = jsonDoc.toVariant().toMap();
if (content.contains("extends")) {
QVariantMap parentContent = loadInterface(content.value("extends").toString());
QVariantList statesList = content.value("states").toList();
statesList.append(parentContent.value("states").toList());
content["states"] = statesList;
QVariantList actionsList = content.value("actions").toList();
actionsList.append(parentContent.value("actions").toList());
content["actions"] = actionsList;
QVariantList eventsList = content.value("events").toList();
eventsList.append(parentContent.value("events").toList());
content["events"] = eventsList;
iface = loadInterface(content.value("extends").toString());
}
return content;
StateTypes stateTypes;
ActionTypes actionTypes;
EventTypes eventTypes;
foreach (const QVariant &stateVariant, content.value("states").toList()) {
StateType stateType(StateTypeId::fromUuid(QUuid()));
stateType.setName(stateVariant.toMap().value("name").toString());
stateType.setType(QVariant::nameToType(stateVariant.toMap().value("type").toByteArray()));
stateType.setPossibleValues(stateVariant.toMap().value("allowedValues").toList());
stateType.setMinValue(stateVariant.toMap().value("minValue"));
stateType.setMaxValue(stateVariant.toMap().value("maxValue"));
stateTypes.append(stateType);
EventType stateChangeEventType(EventTypeId::fromUuid(QUuid()));
stateChangeEventType.setName(stateType.name());
ParamType stateChangeEventParamType;
stateChangeEventParamType.setName(stateType.name());
stateChangeEventParamType.setType(stateType.type());
stateChangeEventParamType.setAllowedValues(stateType.possibleValues());
stateChangeEventParamType.setMinValue(stateType.minValue());
stateChangeEventParamType.setMaxValue(stateType.maxValue());
stateChangeEventType.setParamTypes(ParamTypes() << stateChangeEventParamType);
eventTypes.append(stateChangeEventType);
if (stateVariant.toMap().value("writable", false).toBool()) {
ActionType stateChangeActionType(ActionTypeId::fromUuid(QUuid()));
stateChangeActionType.setName(stateType.name());
stateChangeActionType.setParamTypes(ParamTypes() << stateChangeEventParamType);
actionTypes.append(stateChangeActionType);
}
}
foreach (const QVariant &actionVariant, content.value("actions").toList()) {
ActionType actionType(ActionTypeId::fromUuid(QUuid()));
actionType.setName(actionVariant.toMap().value("name").toString());
ParamTypes paramTypes;
foreach (const QVariant &actionParamVariant, actionVariant.toMap().value("params").toList()) {
ParamType paramType;
paramType.setName(actionParamVariant.toMap().value("name").toString());
paramType.setType(QVariant::nameToType(actionParamVariant.toMap().value("type").toByteArray()));
paramType.setAllowedValues(actionParamVariant.toMap().value("allowedValues").toList());
paramType.setMinValue(actionParamVariant.toMap().value("min"));
paramTypes.append(paramType);
}
actionType.setParamTypes(paramTypes);
actionTypes.append(actionType);
}
foreach (const QVariant &eventVariant, content.value("events").toList()) {
EventType eventType(EventTypeId::fromUuid(QUuid()));
eventType.setName(eventVariant.toMap().value("name").toString());
ParamTypes paramTypes;
foreach (const QVariant &eventParamVariant, eventVariant.toMap().value("params").toList()) {
ParamType paramType;
paramType.setName(eventParamVariant.toMap().value("name").toString());
paramType.setType(QVariant::nameToType(eventParamVariant.toMap().value("type").toByteArray()));
paramType.setAllowedValues(eventParamVariant.toMap().value("allowedValues").toList());
paramType.setMinValue(eventParamVariant.toMap().value("minValue"));
paramType.setMaxValue(eventParamVariant.toMap().value("maxValue"));
paramTypes.append(paramType);
}
eventType.setParamTypes(paramTypes);
eventTypes.append(eventType);
}
return Interface(name, iface.actionTypes() << actionTypes, iface.eventTypes() << eventTypes, iface.stateTypes() << stateTypes);
}
QStringList DevicePlugin::generateInterfaceParentList(const QString &interface)

View File

@ -25,11 +25,11 @@
#define DEVICEPLUGIN_H
#include "devicemanager.h"
#include "deviceclass.h"
#include "libguh.h"
#include "typeutils.h"
#include "types/deviceclass.h"
#include "types/event.h"
#include "types/action.h"
#include "types/vendor.h"
@ -119,7 +119,11 @@ private:
QPair<bool, DeviceClass::BasicTag> loadAndVerifyBasicTag(const QString &basicTag) const;
QPair<bool, DeviceClass::DeviceIcon> loadAndVerifyDeviceIcon(const QString &deviceIcon) const;
static QVariantMap loadInterface(const QString &name);
// FIXME: This is expensive because it will open all the files.
// Once DeviceManager is in libguh-core this should probably be there too.
// I didn't want to add even more dependencies on the devicemanager into here, so reading the list here for now.
static Interfaces allInterfaces();
static Interface loadInterface(const QString &name);
static QStringList generateInterfaceParentList(const QString &interface);
QTranslator *m_translator = nullptr;

View File

@ -60,6 +60,7 @@ private:
class ActionTypes: public QList<ActionType>
{
public:
ActionTypes() = default;
ActionTypes(const QList<ActionType> &other);
ActionType findByName(const QString &name);
ActionType findById(const ActionTypeId &id);

View File

@ -336,7 +336,7 @@ bool DeviceClass::hasStateType(const StateTypeId &stateTypeId)
/*! Returns the eventTypes of this DeviceClass. \{Device}{Devices} created
from this \l{DeviceClass} must have their events matching to this template. */
QList<EventType> DeviceClass::eventTypes() const
EventTypes DeviceClass::eventTypes() const
{
return m_eventTypes;
}
@ -361,7 +361,7 @@ bool DeviceClass::hasEventType(const EventTypeId &eventTypeId)
/*! Returns the actionTypes of this DeviceClass. \{Device}{Devices} created
from this \l{DeviceClass} must have their actions matching to this template. */
QList<ActionType> DeviceClass::actionTypes() const
ActionTypes DeviceClass::actionTypes() const
{
return m_actionTypes;
}

View File

@ -152,11 +152,11 @@ public:
void setStateTypes(const QList<StateType> &stateTypes);
bool hasStateType(const StateTypeId &stateTypeId);
QList<EventType> eventTypes() const;
EventTypes eventTypes() const;
void setEventTypes(const QList<EventType> &eventTypes);
bool hasEventType(const EventTypeId &eventTypeId);
QList<ActionType> actionTypes() const;
ActionTypes actionTypes() const;
void setActionTypes(const QList<ActionType> &actionTypes);
bool hasActionType(const ActionTypeId &actionTypeId);

View File

@ -31,6 +31,11 @@
An EventDescriptor describes an \l{Event} in order to match it with a \l{guhserver::Rule}.
An EventDescriptor can either be bound to a certain device/eventtype, or to an interface.
If an event is bound to a device, it will only match when the given device fires the given event.
If an event is bound to an interface, it will match the given event for all the devices implementing
the given interface.
\sa Event, EventType, guhserver::Rule
*/
@ -44,6 +49,25 @@ EventDescriptor::EventDescriptor(const EventTypeId &eventTypeId, const DeviceId
{
}
EventDescriptor::EventDescriptor(const QString &interface, const QString &interfaceEvent, const QList<ParamDescriptor> &paramDescriptors):
m_interface(interface),
m_interfaceEvent(interfaceEvent),
m_paramDescriptors(paramDescriptors)
{
}
EventDescriptor::Type EventDescriptor::type() const
{
return (!m_deviceId.isNull() && !m_eventTypeId.isNull()) ? TypeDevice : TypeInterface;
}
/*! Returns true if the EventDescriptor is valid, that is, when it has either enough data to describe a device/eventType or an interface/interfaceEvent pair. */
bool EventDescriptor::isValid() const
{
return (!m_deviceId.isNull() && !m_eventTypeId.isNull()) || (!m_interface.isEmpty() && !m_interfaceEvent.isEmpty());
}
/*! Returns the id of the \l{EventType} which describes this Event. */
EventTypeId EventDescriptor::eventTypeId() const
{
@ -56,6 +80,18 @@ DeviceId EventDescriptor::deviceId() const
return m_deviceId;
}
/*! Returns the interface associated with this EventDescriptor. */
QString EventDescriptor::interface() const
{
return m_interface;
}
/*! Returns the interface's event name associated with this EventDescriptor.*/
QString EventDescriptor::interfaceEvent() const
{
return m_interfaceEvent;
}
/*! Returns the parameters of this Event. */
QList<ParamDescriptor> EventDescriptor::paramDescriptors() const
{
@ -97,51 +133,6 @@ bool EventDescriptor::operator ==(const EventDescriptor &other) const
&& paramsMatch;
}
/*! Compare this EventDescriptor to the Event given by \a event.
* Events are equal (returns true) if eventTypeId, deviceId and params match. */
bool EventDescriptor::operator ==(const Event &event) const
{
if (m_eventTypeId != event.eventTypeId() || m_deviceId != event.deviceId()) {
return false;
}
foreach (const ParamDescriptor &paramDescriptor, m_paramDescriptors) {
switch (paramDescriptor.operatorType()) {
case Types::ValueOperatorEquals:
if (event.param(paramDescriptor.paramTypeId()).value() != paramDescriptor.value()) {
return false;
}
break;
case Types::ValueOperatorNotEquals:
if (event.param(paramDescriptor.paramTypeId()).value() == paramDescriptor.value()) {
return false;
}
break;
case Types::ValueOperatorGreater:
if (event.param(paramDescriptor.paramTypeId()).value() <= paramDescriptor.value()) {
return false;
}
break;
case Types::ValueOperatorGreaterOrEqual:
if (event.param(paramDescriptor.paramTypeId()).value() < paramDescriptor.value()) {
return false;
}
break;
case Types::ValueOperatorLess:
if (event.param(paramDescriptor.paramTypeId()).value() >= paramDescriptor.value()) {
return false;
}
break;
case Types::ValueOperatorLessOrEqual:
if (event.param(paramDescriptor.paramTypeId()).value() < paramDescriptor.value()) {
return false;
}
break;
}
}
return true;
}
/*! Writes the eventTypeId and the deviceId of the given \a eventDescriptor to \a dbg. */
QDebug operator<<(QDebug dbg, const EventDescriptor &eventDescriptor)
{

View File

@ -36,22 +36,34 @@
class LIBGUH_EXPORT EventDescriptor
{
public:
enum Type {
TypeDevice,
TypeInterface
};
EventDescriptor(const EventTypeId &eventTypeId, const DeviceId &deviceId, const QList<ParamDescriptor> &paramDescriptors = QList<ParamDescriptor>());
EventDescriptor(const QString &interface, const QString &interfaceEvent, const QList<ParamDescriptor> &paramDescriptors = QList<ParamDescriptor>());
Type type() const;
bool isValid() const;
EventTypeId eventTypeId() const;
DeviceId deviceId() const;
QString interface() const;
QString interfaceEvent() const;
QList<ParamDescriptor> paramDescriptors() const;
void setParamDescriptors(const QList<ParamDescriptor> &paramDescriptors);
ParamDescriptor paramDescriptor(const ParamTypeId &paramTypeId) const;
bool operator ==(const EventDescriptor &other) const;
bool operator ==(const Event &event) const;
private:
EventTypeId m_eventTypeId;
DeviceId m_deviceId;
QString m_interface;
QString m_interfaceEvent;
QList<ParamDescriptor> m_paramDescriptors;
};

View File

@ -69,6 +69,7 @@ private:
class EventTypes: public QList<EventType>
{
public:
EventTypes() = default;
EventTypes(const QList<EventType> &other);
EventType findByName(const QString &name);
EventType findById(const EventTypeId &id);

View File

@ -0,0 +1,74 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2017 Michael Zanetti <michael.zanetti@guh.io> *
* *
* This file is part of guh. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "interface.h"
Interface::Interface(const QString &name, const ActionTypes &actionTypes, const EventTypes &eventTypes, const StateTypes &stateTypes):
m_name(name),
m_actionTypes(actionTypes),
m_eventTypes(eventTypes),
m_stateTypes(stateTypes)
{
}
QString Interface::name() const
{
return m_name;
}
ActionTypes Interface::actionTypes() const
{
return m_actionTypes;
}
EventTypes Interface::eventTypes() const
{
return m_eventTypes;
}
StateTypes Interface::stateTypes() const
{
return m_stateTypes;
}
bool Interface::isValid() const
{
return !m_name.isEmpty();
}
Interfaces::Interfaces(const QList<Interface> &other)
{
foreach (const Interface &iface, other) {
append(iface);
}
}
Interface Interfaces::findByName(const QString &name)
{
foreach (const Interface &interface, *this) {
if (interface.name() == name) {
return interface;
}
}
return Interface();
}

58
libguh/types/interface.h Normal file
View File

@ -0,0 +1,58 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2017 Michael Zanetti <michael.zanetti@guh.io> *
* *
* This file is part of guh. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef INTERFACE_H
#define INTERFACE_H
#include "eventtype.h"
#include "actiontype.h"
#include "statetype.h"
class Interface{
public:
Interface() = default;
Interface(const QString &name, const ActionTypes &actionTypes, const EventTypes &eventTypes, const StateTypes &stateTypes);
QString name() const;
ActionTypes actionTypes() const;
EventTypes eventTypes() const;
StateTypes stateTypes() const;
bool isValid() const;
private:
QUuid m_id;
QString m_name;
ActionTypes m_actionTypes;
EventTypes m_eventTypes;
StateTypes m_stateTypes;
};
class Interfaces: public QList<Interface>
{
public:
Interfaces() = default;
Interfaces(const QList<Interface> &other);
Interface findByName(const QString &name);
};
#endif // INTERFACE_H

View File

@ -38,18 +38,31 @@
#include "ruleaction.h"
/*! Constructs a RuleAction with the given by \a actionTypeId and \a deviceId. */
RuleAction::RuleAction(const ActionTypeId &actionTypeId, const DeviceId &deviceId) :
RuleAction::RuleAction(const ActionTypeId &actionTypeId, const DeviceId &deviceId, const RuleActionParamList &params):
m_id(ActionId::createActionId()),
m_actionTypeId(actionTypeId),
m_deviceId(deviceId)
m_deviceId(deviceId),
m_ruleActionParams(params)
{
}
/*! Constructs a RuleAction with the given by \a interface and \a interfaceAction. */
RuleAction::RuleAction(const QString &interface, const QString &interfaceAction, const RuleActionParamList &params) :
m_interface(interface),
m_interfaceAction(interfaceAction),
m_ruleActionParams(params)
{
}
/*! Constructs a copy of the given \a other RuleAction. */
RuleAction::RuleAction(const RuleAction &other) :
m_id(other.id()),
m_actionTypeId(other.actionTypeId()),
m_deviceId(other.deviceId()),
m_interface(other.interface()),
m_interfaceAction(other.interfaceAction()),
m_ruleActionParams(other.ruleActionParams())
{
@ -64,7 +77,13 @@ ActionId RuleAction::id() const
/*! Return true, if the actionTypeId and the deviceId of this RuleAction are valid (set).*/
bool RuleAction::isValid() const
{
return !m_actionTypeId.isNull() && !m_deviceId.isNull();
return (!m_actionTypeId.isNull() && !m_deviceId.isNull()) || (!m_interface.isEmpty() && !m_interfaceAction.isEmpty());
}
/*! Returns whether this RuleAction is targetting a specific device or rather an interface. */
RuleAction::Type RuleAction::type() const
{
return (!m_actionTypeId.isNull() && !m_deviceId.isNull()) ? TypeDevice : TypeInterface;
}
/*! Return true, if this RuleAction contains a \l{RuleActionParam} which is based on an EventTypeId.*/
@ -103,6 +122,18 @@ DeviceId RuleAction::deviceId() const
return m_deviceId;
}
/*! Returns the name of the interface associated with this RuleAction. */
QString RuleAction::interface() const
{
return m_interface;
}
/*! Returns the name of the action of the associated interface. */
QString RuleAction::interfaceAction() const
{
return m_interfaceAction;
}
/*! Returns the \l{RuleActionParamList} of this RuleAction.
* \sa RuleActionParam, */
RuleActionParamList RuleAction::ruleActionParams() const
@ -118,7 +149,7 @@ void RuleAction::setRuleActionParams(const RuleActionParamList &ruleActionParams
}
/*! Returns the \l{RuleActionParam} of this RuleAction with the given \a ruleActionParamTypeId.
* If there is no \l{RuleActionParam} with th given name an invalid \l{RuleActionParam} will be returnend.
* If there is no \l{RuleActionParam} with th given id an invalid \l{RuleActionParam} will be returnend.
* \sa RuleActionParam, */
RuleActionParam RuleAction::ruleActionParam(const ParamTypeId &ruleActionParamTypeId) const
{
@ -130,6 +161,19 @@ RuleActionParam RuleAction::ruleActionParam(const ParamTypeId &ruleActionParamTy
return RuleActionParam(QString());
}
/*! Returns the \l{RuleActionParam} of this RuleAction with the given \a ruleActionParamName.
* If there is no \l{RuleActionParam} with th given name an invalid \l{RuleActionParam} will be returnend.
* \sa RuleActionParam, */
RuleActionParam RuleAction::ruleActionParam(const QString &ruleActionParamName) const
{
foreach (const RuleActionParam &ruleActionParam, m_ruleActionParams) {
if (ruleActionParam.paramName() == ruleActionParamName) {
return ruleActionParam;
}
}
return RuleActionParam(QString());
}
/*! Copy the data to a \l{RuleAction} from an \a other rule action. */
void RuleAction::operator=(const RuleAction &other)
{

View File

@ -31,12 +31,19 @@
class LIBGUH_EXPORT RuleAction
{
public:
explicit RuleAction(const ActionTypeId &actionTypeId = ActionTypeId(), const DeviceId &deviceId = DeviceId());
enum Type {
TypeDevice,
TypeInterface
};
explicit RuleAction(const ActionTypeId &actionTypeId = ActionTypeId(), const DeviceId &deviceId = DeviceId(), const RuleActionParamList &params = RuleActionParamList());
explicit RuleAction(const QString &interface, const QString &interfaceAction, const RuleActionParamList &params = RuleActionParamList());
RuleAction(const RuleAction &other);
ActionId id() const;
bool isValid() const;
Type type() const;
bool isEventBased() const;
Action toAction() const;
@ -44,9 +51,13 @@ public:
ActionTypeId actionTypeId() const;
DeviceId deviceId() const;
QString interface() const;
QString interfaceAction() const;
RuleActionParamList ruleActionParams() const;
void setRuleActionParams(const RuleActionParamList &ruleActionParams);
RuleActionParam ruleActionParam(const ParamTypeId &ruleActionParamTypeId) const;
RuleActionParam ruleActionParam(const QString &ruleActionParamName) const;
void operator=(const RuleAction &other);
@ -54,6 +65,8 @@ private:
ActionId m_id;
ActionTypeId m_actionTypeId;
DeviceId m_deviceId;
QString m_interface;
QString m_interfaceAction;
RuleActionParamList m_ruleActionParams;
};

View File

@ -57,12 +57,28 @@ RuleActionParam::RuleActionParam(const ParamTypeId &paramTypeId, const QVariant
{
}
/*! Constructs a \l{RuleActionParam} with the given \a paramName, \a value, \a eventTypeId and \a eventParamTypeId.
* \sa Param, Event, */
RuleActionParam::RuleActionParam(const QString &paramName, const QVariant &value, const EventTypeId &eventTypeId, const ParamTypeId &eventParamTypeId):
m_paramName(paramName),
m_value(value),
m_eventTypeId(eventTypeId),
m_eventParamTypeId(eventParamTypeId)
{
}
/*! Returns the \l ParamTypeId of this \l RuleActionParam. */
ParamTypeId RuleActionParam::paramTypeId() const
{
return m_paramTypeId;
}
QString RuleActionParam::paramName() const
{
return m_paramName;
}
/*! Returns the eventParamTypeId of this RuleActionParam. */
ParamTypeId RuleActionParam::eventParamTypeId() const
{
@ -136,6 +152,17 @@ bool RuleActionParamList::hasParam(const ParamTypeId &ruleActionParamTypeId) con
return m_ids.contains(ruleActionParamTypeId);
}
/*! Returns true if this \l{RuleActionParamList} contains a \l{RuleActionParam} with the given \a ruleActionParamName. */
bool RuleActionParamList::hasParam(const QString &ruleActionParamName) const
{
foreach (const RuleActionParam &param, *this) {
if (param.paramName() == ruleActionParamName) {
return true;
}
}
return false;
}
/*! Returns the value of the \l{RuleActionParam} with the given \a ruleActionParamTypeId. */
QVariant RuleActionParamList::paramValue(const ParamTypeId &ruleActionParamTypeId) const
{

View File

@ -36,9 +36,11 @@ class LIBGUH_EXPORT RuleActionParam
{
public:
RuleActionParam(const Param &param = Param());
RuleActionParam(const ParamTypeId &paramTypeId, const QVariant &value = QVariant(), const EventTypeId &eventTypeId = EventTypeId(), const ParamTypeId &eventParamName = ParamTypeId());
RuleActionParam(const ParamTypeId &paramTypeId, const QVariant &value = QVariant(), const EventTypeId &eventTypeId = EventTypeId(), const ParamTypeId &eventParamTypeId = ParamTypeId());
RuleActionParam(const QString &paramName, const QVariant &value = QVariant(), const EventTypeId &eventTypeId = EventTypeId(), const ParamTypeId &eventParamTypeId = ParamTypeId());
ParamTypeId paramTypeId() const;
QString paramName() const;
ParamTypeId eventParamTypeId() const;
void setEventParamTypeId(const ParamTypeId &eventParamTypeId);
@ -53,6 +55,7 @@ public:
private:
ParamTypeId m_paramTypeId;
QString m_paramName;
QVariant m_value;
EventTypeId m_eventTypeId;
ParamTypeId m_eventParamTypeId;
@ -65,6 +68,7 @@ class LIBGUH_EXPORT RuleActionParamList: public QList<RuleActionParam>
{
public:
bool hasParam(const ParamTypeId &ruleActionParamTypeId) const;
bool hasParam(const QString &ruleActionParamName) const;
QVariant paramValue(const ParamTypeId &ruleActionParamName) const;
bool setParamValue(const ParamTypeId &ruleActionParamTypeId, const QVariant &value);
RuleActionParamList operator<<(const RuleActionParam &ruleActionParam);

View File

@ -92,6 +92,7 @@ private:
class StateTypes: public QList<StateType>
{
public:
StateTypes() = default;
StateTypes(const QList<StateType> &other);
StateType findByName(const QString &name);
StateType findById(const StateTypeId &id);

View File

@ -31,7 +31,7 @@
"name": "mock",
"displayName": "Mock Device",
"deviceIcon": "Tune",
"interfaces": ["gateway", "light", "mediacontroller"],
"interfaces": ["gateway", "light", "mediacontroller", "battery"],
"basicTags": [
"Device",
"Actuator",
@ -91,7 +91,36 @@
"defaultValue": false,
"type": "bool",
"cached": false
},
{
"id": "6c8ab9a6-0164-4795-b829-f4394fe4edc4",
"name": "batteryLevel",
"displayName": "battery level",
"displayNameEvent": "battery level",
"type": "int",
"minValue": 0,
"maxValue": 100,
"defaultValue": 0
},
{
"id": "580bc611-1a55-41f3-996f-8d3ccf543db3",
"name": "batteryCritical",
"displayName": "battery level critical",
"displayNameEvent": "battery level critical",
"type": "bool",
"defaultValue": false
},
{
"id": "064aed0d-da4c-49d4-b236-60f97e98ff84",
"name": "power",
"displayName": "powered",
"displayNameEvent": "powered changed",
"displayNameAction": "set power",
"type": "bool",
"defaultValue": false,
"writable": true
}
],
"eventTypes": [
{

View File

@ -24,8 +24,8 @@
#include "httpdaemon.h"
#include "plugin/device.h"
#include "plugin/deviceclass.h"
#include "plugin/deviceplugin.h"
#include "types/deviceclass.h"
#include "types/statetype.h"
#include "extern-plugininfo.h"
@ -137,7 +137,13 @@ QString HttpDaemon::generateHeader()
QString HttpDaemon::generateWebPage()
{
DeviceClass deviceClass = m_plugin->supportedDevices().first();
DeviceClass deviceClass;
foreach (const DeviceClass &dc, m_plugin->supportedDevices()) {
if (dc.id() == m_device->deviceClassId()) {
deviceClass = dc;
}
}
Q_ASSERT(deviceClass.isValid());
QString body = QString(
"<html>"
@ -156,7 +162,7 @@ QString HttpDaemon::generateWebPage()
for (int i = 0; i < deviceClass.stateTypes().count(); ++i) {
body.append("<tr>");
body.append("<form action=\"/setstate\" method=\"get\">");
const StateType &stateType = deviceClass.stateTypes().at(i);
StateType stateType = deviceClass.stateTypes().at(i);
body.append("<td>" + stateType.name() + "</td>");
body.append(QString("<td><input type='input'' name='%1' value='%2'></td>").arg(stateType.id().toString()).arg(m_device->states().at(i).value().toString()));
body.append("<td><input type=submit value='Set State'/></td>");
@ -170,14 +176,14 @@ QString HttpDaemon::generateWebPage()
body.append("<table>");
for (int i = 0; i < deviceClass.eventTypes().count(); ++i) {
const EventType &eventType = deviceClass.eventTypes().at(i);
EventType eventType = deviceClass.eventTypes().at(i);
body.append(QString(
"<tr>"
"<form action=\"/generateevent\" method=\"get\">"
"<td>%1<input type='hidden' name='eventtypeid' value='%2'/></td>"
"<td>").arg(eventType.name()).arg(eventType.id().toString()));
if (!eventType.name().endsWith(" changed")) {
body.append("<input type='submit' value='Generate'/>");
if (!eventType.displayName().endsWith(" changed")) {
body.append(QStringLiteral("<input type='submit' value='Generate'/>"));
}
body.append("</td>"
"</form>"
@ -211,7 +217,6 @@ QString HttpDaemon::generateWebPage()
}
body.append("</table>");
body.append("</body></html>\n");
return generateHeader() + body;

View File

@ -1,4 +1,4 @@
1.0
1.1
{
"methods": {
"Actions.ExecuteAction": {
@ -1152,8 +1152,10 @@
]
},
"EventDescriptor": {
"deviceId": "Uuid",
"eventTypeId": "Uuid",
"o:deviceId": "Uuid",
"o:eventTypeId": "Uuid",
"o:interface": "String",
"o:interfaceEvent": "String",
"o:paramDescriptors": [
"$ref:ParamDescriptor"
]
@ -1325,8 +1327,10 @@
"timeDescriptor": "$ref:TimeDescriptor"
},
"RuleAction": {
"actionTypeId": "Uuid",
"deviceId": "Uuid",
"o:actionTypeId": "Uuid",
"o:deviceId": "Uuid",
"o:interface": "String",
"o:interfaceAction": "String",
"o:ruleActionParams": [
"$ref:RuleActionParam"
]
@ -1334,8 +1338,9 @@
"RuleActionParam": {
"o:eventParamTypeId": "Uuid",
"o:eventTypeId": "Uuid",
"o:value": "$ref:BasicType",
"paramTypeId": "Uuid"
"o:paramName": "String",
"o:paramTypeId": "Uuid",
"o:value": "$ref:BasicType"
},
"RuleDescription": {
"active": "Bool",
@ -1364,7 +1369,8 @@
"RuleErrorInvalidCalendarItem",
"RuleErrorInvalidTimeEventItem",
"RuleErrorContainsEventBasesAction",
"RuleErrorNoExitActions"
"RuleErrorNoExitActions",
"RuleErrorInterfaceNotFound"
],
"ServerConfiguration": {
"address": "String",

View File

@ -241,9 +241,13 @@ void TestDevices::verifyInterfaces()
QVERIFY(!mockDevice.isEmpty());
QVariantList interfaces = mockDevice.value("interfaces").toList();
// Must contain gateway, but must not contain anything else as device manager should filter it away
QCOMPARE(interfaces.count() == 1, true);
// Must contain gateway, light and battery, but must not contain mediacontroller as the device manager should filter
// that away because it doesn't implement all the required states.
QCOMPARE(interfaces.count(), 3);
QVERIFY(interfaces.contains("gateway"));
QVERIFY(interfaces.contains("battery"));
QVERIFY(interfaces.contains("light"));
QVERIFY(!interfaces.contains("mediacontroller"));
}
void TestDevices::addConfiguredDevice_data()
@ -631,25 +635,33 @@ void TestDevices::parentChildDevices()
void TestDevices::getActionTypes_data()
{
QTest::addColumn<DeviceClassId>("deviceClassId");
QTest::addColumn<int>("resultCount");
QTest::addColumn<QList<ActionTypeId> >("actionTypeTestData");
QTest::newRow("valid deviceclass") << mockDeviceClassId << 5;
QTest::newRow("invalid deviceclass") << DeviceClassId("094f8024-5caa-48c1-ab6a-de486a92088f") << 0;
QTest::newRow("valid deviceclass") << mockDeviceClassId
<< (QList<ActionTypeId>() << mockActionIdAsync << mockActionIdAsyncFailing << mockActionIdFailing << mockActionIdNoParams << mockActionIdPower << mockActionIdWithParams);
QTest::newRow("invalid deviceclass") << DeviceClassId("094f8024-5caa-48c1-ab6a-de486a92088f") << QList<ActionTypeId>();
}
void TestDevices::getActionTypes()
{
QFETCH(DeviceClassId, deviceClassId);
QFETCH(int, resultCount);
QFETCH(QList<ActionTypeId>, actionTypeTestData);
QVariantMap params;
params.insert("deviceClassId", deviceClassId);
QVariant response = injectAndWait("Devices.GetActionTypes", params);
QVariantList actionTypes = response.toMap().value("params").toMap().value("actionTypes").toList();
QCOMPARE(actionTypes.count(), resultCount);
if (resultCount > 0) {
QCOMPARE(actionTypes.first().toMap().value("id").toString(), mockActionIdWithParams.toString());
QCOMPARE(actionTypes.count(), actionTypeTestData.count());
foreach (const ActionTypeId &testDataId, actionTypeTestData) {
bool found = false;
foreach (const QVariant &at, actionTypes) {
if (testDataId.toString() == at.toMap().value("id").toString()) {
found = true;
break;
}
}
QVERIFY(found);
}
}
@ -658,7 +670,7 @@ void TestDevices::getEventTypes_data()
QTest::addColumn<DeviceClassId>("deviceClassId");
QTest::addColumn<int>("resultCount");
QTest::newRow("valid deviceclass") << mockDeviceClassId << 4;
QTest::newRow("valid deviceclass") << mockDeviceClassId << 7;
QTest::newRow("invalid deviceclass") << DeviceClassId("094f8024-5caa-48c1-ab6a-de486a92088f") << 0;
}
@ -683,7 +695,7 @@ void TestDevices::getStateTypes_data()
QTest::addColumn<DeviceClassId>("deviceClassId");
QTest::addColumn<int>("resultCount");
QTest::newRow("valid deviceclass") << mockDeviceClassId << 2;
QTest::newRow("valid deviceclass") << mockDeviceClassId << 5;
QTest::newRow("invalid deviceclass") << DeviceClassId("094f8024-5caa-48c1-ab6a-de486a92088f") << 0;
}
@ -784,7 +796,7 @@ void TestDevices::getStateValues()
QCOMPARE(response.toMap().value("params").toMap().value("deviceError").toString(), JsonTypes::deviceErrorToString(statusCode));
if (statusCode == DeviceManager::DeviceErrorNoError) {
QVariantList values = response.toMap().value("params").toMap().value("values").toList();
QCOMPARE(values.count(), 2); // Mock device has two states...
QCOMPARE(values.count(), 5); // Mock device has two states...
}
}
@ -1231,6 +1243,8 @@ void TestDevices::removeAutoDevice()
QVERIFY2(devices.count() > 0, "There needs to be at least one auto-created Mock Device for this test");
device = devices.first();
DeviceClass dc = GuhCore::instance()->deviceManager()->findDeviceClass(device->deviceClassId());
// trigger disappear signal in mock device
spy.clear();
port = device->paramValue(httpportParamTypeId).toInt();

View File

@ -56,6 +56,8 @@ EventTypeId mockEvent1Id = EventTypeId("45bf3752-0fc6-46b9-89fd-ffd878b5b22b");
EventTypeId mockEvent2Id = EventTypeId("863d5920-b1cf-4eb9-88bd-8f7b8583b1cf");
StateTypeId mockIntStateId = StateTypeId("80baec19-54de-4948-ac46-31eabfaceb83");
StateTypeId mockBoolStateId = StateTypeId("9dd6a97c-dfd1-43dc-acbd-367932742310");
StateTypeId mockBatteryCriticalStateId = StateTypeId("580bc611-1a55-41f3-996f-8d3ccf543db3");
ActionTypeId mockActionIdPower = ActionTypeId("064aed0d-da4c-49d4-b236-60f97e98ff84");
ActionTypeId mockActionIdWithParams = ActionTypeId("dea0f4e1-65e3-4981-8eaa-2701c53a9185");
ActionTypeId mockActionIdNoParams = ActionTypeId("defd3ed6-1a0d-400b-8879-a0202cf39935");
ActionTypeId mockActionIdAsync = ActionTypeId("fbae06d3-7666-483e-a39e-ec50fe89054e");

View File

@ -47,6 +47,7 @@ extern DeviceClassId mockDeviceDiscoveryClassId;
extern DeviceClassId mockDeviceAsyncSetupClassId;
extern DeviceClassId mockDeviceBrokenClassId;
extern DeviceClassId mockDeviceBrokenAsyncSetupClassId;
extern ActionTypeId mockActionIdPower;
extern ActionTypeId mockActionIdWithParams;
extern ActionTypeId mockActionIdNoParams;
extern ActionTypeId mockActionIdAsync;
@ -55,6 +56,7 @@ extern ActionTypeId mockActionIdAsyncFailing;
extern EventTypeId mockEvent1Id;
extern EventTypeId mockEvent2Id;
extern StateTypeId mockIntStateId;
extern StateTypeId mockBatteryCriticalStateId;
extern StateTypeId mockBoolStateId;
// ParamTypes from mock devices

View File

@ -99,6 +99,8 @@ private slots:
void testRuleActionParams_data();
void testRuleActionParams();
void testInterfaceBasedRule();
void testHousekeeping_data();
void testHousekeeping();
@ -488,7 +490,7 @@ void TestRules::addRemoveRules_data()
QTest::newRow("valid rule. diabled, 1 EventDescriptor, StateEvaluator, 1 Action, name") << false << validActionNoParams << QVariantMap() << validEventDescriptor1 << QVariantList() << validStateEvaluator << RuleEngine::RuleErrorNoError << true << "TestRule";
QTest::newRow("valid rule. 2 EventDescriptors, 1 Action, name") << true << validActionNoParams << QVariantMap() << QVariantMap() << eventDescriptorList << validStateEvaluator << RuleEngine::RuleErrorNoError << true << "TestRule";
QTest::newRow("invalid action") << true << invalidAction << QVariantMap() << validEventDescriptor1 << QVariantList() << validStateEvaluator << RuleEngine::RuleErrorActionTypeNotFound << false << "TestRule";
QTest::newRow("invalid event descriptor") << true << validActionNoParams << QVariantMap() << invalidEventDescriptor << QVariantList() << validStateEvaluator << RuleEngine::RuleErrorDeviceNotFound << false << "TestRule";
QTest::newRow("invalid event descriptor") << true << validActionNoParams << QVariantMap() << invalidEventDescriptor << QVariantList() << validStateEvaluator << RuleEngine::RuleErrorEventTypeNotFound << false << "TestRule";
QTest::newRow("invalid StateDescriptor") << true << validActionNoParams << QVariantMap() << validEventDescriptor1 << QVariantList() << invalidStateEvaluator << RuleEngine::RuleErrorInvalidParameter << true << "TestRule";
}
@ -2065,6 +2067,44 @@ void TestRules::testRuleActionParams()
verifyRuleError(response, error);
}
void TestRules::testInterfaceBasedRule()
{
QVariantMap powerAction;
powerAction.insert("interface", "light");
powerAction.insert("interfaceAction", "power");
QVariantMap powerActionParam;
powerActionParam.insert("paramName", "power");
powerActionParam.insert("value", true);
powerAction.insert("ruleActionParams", QVariantList() << powerActionParam);
QVariantMap lowBatteryEvent;
lowBatteryEvent.insert("interface", "battery");
lowBatteryEvent.insert("interfaceEvent", "batteryCritical");
QVariantMap addRuleParams;
addRuleParams.insert("name", "TestInterfaceBasedRule");
addRuleParams.insert("enabled", true);
addRuleParams.insert("actions", QVariantList() << powerAction);
addRuleParams.insert("eventDescriptors", QVariantList() << lowBatteryEvent);
QVariant response = injectAndWait("Rules.AddRule", addRuleParams);
// Change the state
QNetworkAccessManager nam;
QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*)));
// state battery critical state to true
qDebug() << "setting battery critical state to true";
QNetworkRequest request(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockBatteryCriticalStateId.toString()).arg(true)));
QNetworkReply *reply = nam.get(request);
spy.wait();
QCOMPARE(spy.count(), 1);
reply->deleteLater();
qDebug() << "response" << response;
}
void TestRules::testHousekeeping_data()
{
QTest::addColumn<bool>("testAction");