diff --git a/libnymea/integrations/pluginmetadata.cpp b/libnymea/integrations/pluginmetadata.cpp index cd71154c..10390c4d 100644 --- a/libnymea/integrations/pluginmetadata.cpp +++ b/libnymea/integrations/pluginmetadata.cpp @@ -634,29 +634,29 @@ void PluginMetadata::parse(const QJsonObject &jsonObject) ActionTypes actionTypes(thingClass.actionTypes()); EventTypes eventTypes(thingClass.eventTypes()); - foreach (const StateType &ifaceStateType, iface.stateTypes()) { + foreach (const InterfaceStateType &ifaceStateType, iface.stateTypes()) { StateType stateType = stateTypes.findByName(ifaceStateType.name()); if (stateType.id().isNull()) { - m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but doesn't implement state \"" + ifaceStateType.name() + "\""); - hasError = true; - continue; + if (!ifaceStateType.optional()) { + m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but doesn't implement state \"" + ifaceStateType.name() + "\""); + hasError = true; + } else { + continue; + } } if (ifaceStateType.type() != stateType.type()) { m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but state \"" + stateType.name() + "\" has not matching type: \"" + QVariant::typeToName(stateType.type()) + "\" != \"" + QVariant::typeToName(ifaceStateType.type()) + "\""); hasError = true; - continue; } if (ifaceStateType.minValue().isValid() && !ifaceStateType.minValue().isNull()) { if (ifaceStateType.minValue().toString() == "any") { if (stateType.minValue().isNull()) { m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but state \"" + stateType.name() + "\" has no minimum value defined."); hasError = true; - continue; } } else if (ifaceStateType.minValue() != stateType.minValue()) { m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but state \"" + stateType.name() + "\" has not matching minimum value: \"" + ifaceStateType.minValue().toString() + "\" != \"" + stateType.minValue().toString() + "\""); hasError = true; - continue; } } if (ifaceStateType.maxValue().isValid() && !ifaceStateType.maxValue().isNull()) { @@ -664,32 +664,32 @@ void PluginMetadata::parse(const QJsonObject &jsonObject) if (stateType.maxValue().isNull()) { m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but state \"" + stateType.name() + "\" has no maximum value defined."); hasError = true; - continue; } } else if (ifaceStateType.maxValue() != stateType.maxValue()) { m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but state \"" + stateType.name() + "\" has not matching maximum value: \"" + ifaceStateType.maxValue().toString() + "\" != \"" + stateType.minValue().toString() + "\""); hasError = true; - continue; } } if (!ifaceStateType.possibleValues().isEmpty() && ifaceStateType.possibleValues() != stateType.possibleValues()) { m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but state \"" + stateType.name() + "\" has not matching allowed values."); hasError = true; - continue; } if (ifaceStateType.unit() != Types::UnitNone && ifaceStateType.unit() != stateType.unit()) { QMetaEnum unitEnum = QMetaEnum::fromType(); m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but state \"" + stateType.name() + "\" has not matching unit: \"" + unitEnum.valueToKey(ifaceStateType.unit()) + "\" != \"" + unitEnum.valueToKey(stateType.unit())); hasError = true; - continue; } } - foreach (const ActionType &ifaceActionType, iface.actionTypes()) { + foreach (const InterfaceActionType &ifaceActionType, iface.actionTypes()) { ActionType actionType = actionTypes.findByName(ifaceActionType.name()); if (actionType.id().isNull()) { - m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but doesn't implement action \"" + ifaceActionType.name() + "\""); - hasError = true; + if (!ifaceActionType.optional()) { + m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but doesn't implement action \"" + ifaceActionType.name() + "\""); + hasError = true; + } else { + continue; + } } foreach (const ParamType &ifaceActionParamType, ifaceActionType.paramTypes()) { ParamType paramType = actionType.paramTypes().findByName(ifaceActionParamType.name()); @@ -711,11 +711,15 @@ void PluginMetadata::parse(const QJsonObject &jsonObject) } } - foreach (const EventType &ifaceEventType, iface.eventTypes()) { + foreach (const InterfaceEventType &ifaceEventType, iface.eventTypes()) { EventType eventType = eventTypes.findByName(ifaceEventType.name()); if (!eventType.isValid()) { - m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but doesn't implement event \"" + ifaceEventType.name() + "\""); - hasError = true; + if (!ifaceEventType.optional()) { + m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but doesn't implement event \"" + ifaceEventType.name() + "\""); + hasError = true; + } else { + continue; + } } foreach (const ParamType &ifaceEventParamType, ifaceEventType.paramTypes()) { ParamType paramType = eventType.paramTypes().findByName(ifaceEventParamType.name()); diff --git a/libnymea/integrations/thingutils.cpp b/libnymea/integrations/thingutils.cpp index 8a1c97bb..7d47ac82 100644 --- a/libnymea/integrations/thingutils.cpp +++ b/libnymea/integrations/thingutils.cpp @@ -189,16 +189,17 @@ Interface ThingUtils::loadInterface(const QString &name) } } - StateTypes stateTypes; - ActionTypes actionTypes; - EventTypes eventTypes; + InterfaceStateTypes stateTypes; + InterfaceActionTypes actionTypes; + InterfaceEventTypes eventTypes; foreach (const QVariant &stateVariant, content.value("states").toList()) { - StateType stateType; + InterfaceStateType stateType; 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")); + stateType.setOptional(stateVariant.toMap().value("optional", false).toBool()); if (stateVariant.toMap().contains("unit")) { QMetaEnum unitEnum = QMetaEnum::fromType(); int enumValue = unitEnum.keyToValue("Unit" + stateVariant.toMap().value("unit").toByteArray()); @@ -210,8 +211,9 @@ Interface ThingUtils::loadInterface(const QString &name) } stateTypes.append(stateType); - EventType stateChangeEventType; + InterfaceEventType stateChangeEventType; stateChangeEventType.setName(stateType.name()); + stateChangeEventType.setOptional(stateType.optional()); ParamType stateChangeEventParamType; stateChangeEventParamType.setName(stateType.name()); stateChangeEventParamType.setType(stateType.type()); @@ -222,16 +224,18 @@ Interface ThingUtils::loadInterface(const QString &name) eventTypes.append(stateChangeEventType); if (stateVariant.toMap().value("writable", false).toBool()) { - ActionType stateChangeActionType; + InterfaceActionType stateChangeActionType; stateChangeActionType.setName(stateType.name()); + stateChangeActionType.setOptional(stateType.optional()); stateChangeActionType.setParamTypes(ParamTypes() << stateChangeEventParamType); actionTypes.append(stateChangeActionType); } } foreach (const QVariant &actionVariant, content.value("actions").toList()) { - ActionType actionType; + InterfaceActionType actionType; actionType.setName(actionVariant.toMap().value("name").toString()); + actionType.setOptional(actionVariant.toMap().value("optional").toBool()); ParamTypes paramTypes; foreach (const QVariant &actionParamVariant, actionVariant.toMap().value("params").toList()) { ParamType paramType; @@ -246,8 +250,9 @@ Interface ThingUtils::loadInterface(const QString &name) } foreach (const QVariant &eventVariant, content.value("events").toList()) { - EventType eventType; + InterfaceEventType eventType; eventType.setName(eventVariant.toMap().value("name").toString()); + eventType.setOptional(eventVariant.toMap().value("optional").toBool()); ParamTypes paramTypes; foreach (const QVariant &eventParamVariant, eventVariant.toMap().value("params").toList()) { ParamType paramType; @@ -267,20 +272,20 @@ Interface ThingUtils::loadInterface(const QString &name) Interface ThingUtils::mergeInterfaces(const Interface &iface1, const Interface &iface2) { - EventTypes eventTypes = iface1.eventTypes(); - foreach (const EventType &et, iface2.eventTypes()) { + InterfaceEventTypes eventTypes = iface1.eventTypes(); + foreach (const InterfaceEventType &et, iface2.eventTypes()) { if (eventTypes.findByName(et.name()).name().isEmpty()) { eventTypes.append(et); } } - StateTypes stateTypes = iface1.stateTypes(); - foreach (const StateType &st, iface2.stateTypes()) { + InterfaceStateTypes stateTypes = iface1.stateTypes(); + foreach (const InterfaceStateType &st, iface2.stateTypes()) { if (stateTypes.findByName(st.name()).name().isEmpty()) { stateTypes.append(st); } } - ActionTypes actionTypes = iface1.actionTypes(); - foreach (const ActionType &at, iface2.actionTypes()) { + InterfaceActionTypes actionTypes = iface1.actionTypes(); + foreach (const InterfaceActionType &at, iface2.actionTypes()) { if (actionTypes.findByName(at.name()).name().isEmpty()) { actionTypes.append(at); } diff --git a/libnymea/libnymea.pro b/libnymea/libnymea.pro index 76ff4cda..2da85aad 100644 --- a/libnymea/libnymea.pro +++ b/libnymea/libnymea.pro @@ -35,6 +35,9 @@ HEADERS += \ types/browseritem.h \ types/browseritemaction.h \ types/browseraction.h \ + types/interfaceactiontype.h \ + types/interfaceeventtype.h \ + types/interfacestatetype.h \ types/mediabrowseritem.h \ types/thingclass.h \ typeutils.h \ @@ -149,6 +152,9 @@ SOURCES += \ types/browseritem.cpp \ types/browseritemaction.cpp \ types/browseraction.cpp \ + types/interfaceactiontype.cpp \ + types/interfaceeventtype.cpp \ + types/interfacestatetype.cpp \ types/mediabrowseritem.cpp \ types/action.cpp \ types/actiontype.cpp \ diff --git a/libnymea/types/interface.cpp b/libnymea/types/interface.cpp index dd73f3ce..83d72e75 100644 --- a/libnymea/types/interface.cpp +++ b/libnymea/types/interface.cpp @@ -30,7 +30,7 @@ #include "interface.h" -Interface::Interface(const QString &name, const ActionTypes &actionTypes, const EventTypes &eventTypes, const StateTypes &stateTypes): +Interface::Interface(const QString &name, const InterfaceActionTypes &actionTypes, const InterfaceEventTypes &eventTypes, const InterfaceStateTypes &stateTypes): m_name(name), m_actionTypes(actionTypes), m_eventTypes(eventTypes), @@ -44,17 +44,17 @@ QString Interface::name() const return m_name; } -ActionTypes Interface::actionTypes() const +InterfaceActionTypes Interface::actionTypes() const { return m_actionTypes; } -EventTypes Interface::eventTypes() const +InterfaceEventTypes Interface::eventTypes() const { return m_eventTypes; } -StateTypes Interface::stateTypes() const +InterfaceStateTypes Interface::stateTypes() const { return m_stateTypes; } diff --git a/libnymea/types/interface.h b/libnymea/types/interface.h index a9c6d834..e4164483 100644 --- a/libnymea/types/interface.h +++ b/libnymea/types/interface.h @@ -31,28 +31,29 @@ #ifndef INTERFACE_H #define INTERFACE_H -#include "eventtype.h" -#include "actiontype.h" -#include "statetype.h" +#include "interfaceeventtype.h" +#include "interfaceactiontype.h" +#include "interfacestatetype.h" class Interface{ public: Interface() = default; - Interface(const QString &name, const ActionTypes &actionTypes, const EventTypes &eventTypes, const StateTypes &stateTypes); + Interface(const QString &name, const InterfaceActionTypes &actionTypes, const InterfaceEventTypes &eventTypes, const InterfaceStateTypes &stateTypes); QString name() const; - ActionTypes actionTypes() const; - EventTypes eventTypes() const; - StateTypes stateTypes() const; + InterfaceActionTypes actionTypes() const; + InterfaceEventTypes eventTypes() const; + InterfaceStateTypes stateTypes() const; bool isValid() const; private: QUuid m_id; QString m_name; - ActionTypes m_actionTypes; - EventTypes m_eventTypes; - StateTypes m_stateTypes; + InterfaceActionTypes m_actionTypes; + InterfaceEventTypes m_eventTypes; + InterfaceStateTypes m_stateTypes; + bool m_optional = false; }; class LIBNYMEA_EXPORT Interfaces: public QList diff --git a/libnymea/types/interfaceactiontype.cpp b/libnymea/types/interfaceactiontype.cpp new file mode 100644 index 00000000..ef66d56e --- /dev/null +++ b/libnymea/types/interfaceactiontype.cpp @@ -0,0 +1,32 @@ +#include "interfaceactiontype.h" + +InterfaceActionType::InterfaceActionType() +{ + +} + +bool InterfaceActionType::optional() const +{ + return m_optional; +} + +void InterfaceActionType::setOptional(bool optional) +{ + m_optional = optional; +} + +InterfaceActionTypes::InterfaceActionTypes(const QList &other): + QList(other) +{ + +} + +InterfaceActionType InterfaceActionTypes::findByName(const QString &name) +{ + foreach (const InterfaceActionType &iat, *this) { + if (iat.name() == name) { + return iat; + } + } + return InterfaceActionType(); +} diff --git a/libnymea/types/interfaceactiontype.h b/libnymea/types/interfaceactiontype.h new file mode 100644 index 00000000..1e650722 --- /dev/null +++ b/libnymea/types/interfaceactiontype.h @@ -0,0 +1,26 @@ +#ifndef INTERFACEACTIONTYPE_H +#define INTERFACEACTIONTYPE_H + +#include "actiontype.h" + +class InterfaceActionType: public ActionType +{ +public: + InterfaceActionType(); + + bool optional() const; + void setOptional(bool optional); + +private: + bool m_optional = false; +}; + +class InterfaceActionTypes: public QList +{ +public: + InterfaceActionTypes() = default; + InterfaceActionTypes(const QList &other); + InterfaceActionType findByName(const QString &name); +}; + +#endif // INTERFACEACTIONTYPE_H diff --git a/libnymea/types/interfaceeventtype.cpp b/libnymea/types/interfaceeventtype.cpp new file mode 100644 index 00000000..1ee3f9ac --- /dev/null +++ b/libnymea/types/interfaceeventtype.cpp @@ -0,0 +1,32 @@ +#include "interfaceeventtype.h" + +InterfaceEventType::InterfaceEventType() +{ + +} + +bool InterfaceEventType::optional() const +{ + return m_optional; +} + +void InterfaceEventType::setOptional(bool optional) +{ + m_optional = optional; +} + +InterfaceEventTypes::InterfaceEventTypes(const QList &other): + QList(other) +{ + +} + +InterfaceEventType InterfaceEventTypes::findByName(const QString &name) +{ + foreach (const InterfaceEventType &iet, *this) { + if (iet.name() == name) { + return iet; + } + } + return InterfaceEventType(); +} diff --git a/libnymea/types/interfaceeventtype.h b/libnymea/types/interfaceeventtype.h new file mode 100644 index 00000000..c81305b4 --- /dev/null +++ b/libnymea/types/interfaceeventtype.h @@ -0,0 +1,26 @@ +#ifndef INTERFACEEVENTTYPE_H +#define INTERFACEEVENTTYPE_H + +#include "eventtype.h" + +class InterfaceEventType: public EventType +{ +public: + InterfaceEventType(); + + bool optional() const; + void setOptional(bool optional); + +private: + bool m_optional = false; +}; + +class InterfaceEventTypes: public QList +{ +public: + InterfaceEventTypes() = default; + InterfaceEventTypes(const QList &other); + InterfaceEventType findByName(const QString &name); +}; + +#endif // INTERFACEEVENTTYPE_H diff --git a/libnymea/types/interfacestatetype.cpp b/libnymea/types/interfacestatetype.cpp new file mode 100644 index 00000000..97981bd1 --- /dev/null +++ b/libnymea/types/interfacestatetype.cpp @@ -0,0 +1,32 @@ +#include "interfacestatetype.h" + +InterfaceStateType::InterfaceStateType() +{ + +} + +bool InterfaceStateType::optional() const +{ + return m_optional; +} + +void InterfaceStateType::setOptional(bool optional) +{ + m_optional = optional; +} + +InterfaceStateTypes::InterfaceStateTypes(const QList &other): + QList(other) +{ + +} + +InterfaceStateType InterfaceStateTypes::findByName(const QString &name) +{ + foreach (const InterfaceStateType &ist, *this) { + if (ist.name() == name) { + return ist; + } + } + return InterfaceStateType(); +} diff --git a/libnymea/types/interfacestatetype.h b/libnymea/types/interfacestatetype.h new file mode 100644 index 00000000..247f3b48 --- /dev/null +++ b/libnymea/types/interfacestatetype.h @@ -0,0 +1,26 @@ +#ifndef INTERFACESTATETYPE_H +#define INTERFACESTATETYPE_H + +#include "statetype.h" + +class InterfaceStateType: public StateType +{ +public: + InterfaceStateType(); + + bool optional() const; + void setOptional(bool optional); + +private: + bool m_optional = false; +}; + +class InterfaceStateTypes: public QList +{ +public: + InterfaceStateTypes() = default; + InterfaceStateTypes(const QList &other); + InterfaceStateType findByName(const QString &name); +}; + +#endif // INTERFACESTATETYPE_H