mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
Moved effect configurations from the config file to effect directory
Former-commit-id: b8db13f25b93a0007adf613f0310a1cfbb6b8224
This commit is contained in:
parent
f1acf5a9e4
commit
0537fdc741
@ -331,6 +331,11 @@
|
||||
}
|
||||
],
|
||||
|
||||
"effects" :
|
||||
{
|
||||
"paths" : ["/home/pi/hyperion/effects"]
|
||||
},
|
||||
|
||||
/// The black border configuration, contains the following items:
|
||||
/// * enable : true if the detector should be activated
|
||||
"blackborderdetector" :
|
||||
@ -338,12 +343,10 @@
|
||||
"enable" : true
|
||||
},
|
||||
|
||||
/// The boot-sequence configuration, contains the following items:
|
||||
/// * type : The type of the boot-sequence ('rainbow', 'knightrider', 'none')
|
||||
/// * duration_ms : The length of the boot-sequence [ms]
|
||||
"bootsequence" :
|
||||
{
|
||||
"type" : "Rainbow",
|
||||
"effect" : "rainbow-swirl-fast.json",
|
||||
"path" : "/home/pi/hyperion/effects",
|
||||
"duration_ms" : 3000
|
||||
},
|
||||
|
||||
|
9
effects/knight-rider.json
Executable file
9
effects/knight-rider.json
Executable file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"name" : "Knight rider",
|
||||
"script" : "knight-rider.py",
|
||||
"args" :
|
||||
{
|
||||
"speed" : 1.0,
|
||||
"fadeFactor" : 0.7
|
||||
}
|
||||
}
|
12
effects/mood-blobs-blue.json
Executable file
12
effects/mood-blobs-blue.json
Executable file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name" : "Blue mood blobs",
|
||||
"script" : "mood-blobs.py",
|
||||
"args" :
|
||||
{
|
||||
"rotationTime" : 60.0,
|
||||
"color" : [0,0,255],
|
||||
"hueChange" : 60.0,
|
||||
"blobs" : 5,
|
||||
"reverse" : false
|
||||
}
|
||||
}
|
12
effects/mood-blobs-green.json
Executable file
12
effects/mood-blobs-green.json
Executable file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name" : "Green mood blobs",
|
||||
"script" : "mood-blobs.py",
|
||||
"args" :
|
||||
{
|
||||
"rotationTime" : 60.0,
|
||||
"color" : [0,255,0],
|
||||
"hueChange" : 60.0,
|
||||
"blobs" : 5,
|
||||
"reverse" : false
|
||||
}
|
||||
}
|
12
effects/mood-blobs-red.json
Executable file
12
effects/mood-blobs-red.json
Executable file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name" : "Red mood blobs",
|
||||
"script" : "mood-blobs.py",
|
||||
"args" :
|
||||
{
|
||||
"rotationTime" : 60.0,
|
||||
"color" : [255,0,0],
|
||||
"hueChange" : 60.0,
|
||||
"blobs" : 5,
|
||||
"reverse" : false
|
||||
}
|
||||
}
|
10
effects/rainbow-mood.json
Executable file
10
effects/rainbow-mood.json
Executable file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"name" : "Rainbow mood",
|
||||
"script" : "rainbow-mood.py",
|
||||
"args" :
|
||||
{
|
||||
"rotation-time" : 60.0,
|
||||
"brightness" : 1.0,
|
||||
"reverse" : false
|
||||
}
|
||||
}
|
10
effects/rainbow-swirl-fast.json
Executable file
10
effects/rainbow-swirl-fast.json
Executable file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"name" : "Rainbow swirl fast",
|
||||
"script" : "rainbow-swirl.py",
|
||||
"args" :
|
||||
{
|
||||
"rotation-time" : 3.0,
|
||||
"brightness" : 1.0,
|
||||
"reverse" : false
|
||||
}
|
||||
}
|
10
effects/rainbow-swirl.json
Executable file
10
effects/rainbow-swirl.json
Executable file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"name" : "Rainbow swirl",
|
||||
"script" : "rainbow-swirl.py",
|
||||
"args" :
|
||||
{
|
||||
"rotation-time" : 20.0,
|
||||
"brightness" : 1.0,
|
||||
"reverse" : false
|
||||
}
|
||||
}
|
@ -26,6 +26,8 @@ public:
|
||||
|
||||
const std::list<EffectDefinition> & getEffects() const;
|
||||
|
||||
static bool loadEffectDefinition(const std::string & path, const std::string & effectConfigFile, EffectDefinition &effectDefinition);
|
||||
|
||||
public slots:
|
||||
/// Run the specified effect on the given priority channel and optionally specify a timeout
|
||||
int runEffect(const std::string &effectName, int priority, int timeout = -1);
|
||||
|
@ -5,13 +5,24 @@
|
||||
// Bootsequence includes
|
||||
#include <bootsequence/BootSequenceFactory.h>
|
||||
|
||||
// Effect engine includes
|
||||
#include <effectengine/EffectEngine.h>
|
||||
|
||||
// Local Bootsequence includes
|
||||
#include "EffectBootSequence.h"
|
||||
|
||||
BootSequence * BootSequenceFactory::createBootSequence(Hyperion * hyperion, const Json::Value & jsonConfig)
|
||||
{
|
||||
const std::string script = jsonConfig["script"].asString();
|
||||
const Json::Value args = jsonConfig.get("args", Json::Value(Json::objectValue));
|
||||
const std::string path = jsonConfig["path"].asString();
|
||||
const std::string effectFile = jsonConfig["effect"].asString();
|
||||
const unsigned duration = jsonConfig["duration_ms"].asUInt();
|
||||
return new EffectBootSequence(hyperion, script, args, duration);
|
||||
|
||||
EffectDefinition effect;
|
||||
if (EffectEngine::loadEffectDefinition(path, effectFile, effect))
|
||||
{
|
||||
return new EffectBootSequence(hyperion, effect, duration);
|
||||
}
|
||||
|
||||
std::cerr << "Boot sequence could not be loaded" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
#include "EffectBootSequence.h"
|
||||
|
||||
EffectBootSequence::EffectBootSequence(Hyperion *hyperion, const std::string &script, const Json::Value &args, const unsigned duration_ms) :
|
||||
EffectBootSequence::EffectBootSequence(Hyperion *hyperion, const EffectDefinition &effect, const unsigned duration_ms) :
|
||||
BootSequence(),
|
||||
_hyperion(hyperion),
|
||||
_script(script),
|
||||
_args(args),
|
||||
_effect(effect),
|
||||
_duration_ms(duration_ms)
|
||||
{
|
||||
}
|
||||
@ -15,5 +14,5 @@ EffectBootSequence::~EffectBootSequence()
|
||||
|
||||
void EffectBootSequence::start()
|
||||
{
|
||||
_hyperion->setEffectScript(_script, _args, 0, _duration_ms);
|
||||
_hyperion->setEffectScript(_effect.script, _effect.args, 0, _duration_ms);
|
||||
}
|
||||
|
@ -17,9 +17,10 @@ public:
|
||||
/// duration is the length the effect will run.
|
||||
///
|
||||
/// @param[in] hyperion The Hyperion instance
|
||||
/// @param[in] effect The effect definition
|
||||
/// @param[in] duration_ms The length of the sequence [ms]
|
||||
///
|
||||
EffectBootSequence(Hyperion * hyperion, const std::string & script, const Json::Value & args, const unsigned duration_ms);
|
||||
EffectBootSequence(Hyperion * hyperion, const EffectDefinition & effect, const unsigned duration_ms);
|
||||
virtual ~EffectBootSequence();
|
||||
|
||||
virtual void start();
|
||||
@ -29,10 +30,7 @@ private:
|
||||
Hyperion * _hyperion;
|
||||
|
||||
/// The script to execute
|
||||
const std::string _script;
|
||||
|
||||
/// The arguments of the script
|
||||
const Json::Value _args;
|
||||
const EffectDefinition _effect;
|
||||
|
||||
/// Duration of the boot sequence
|
||||
const unsigned _duration_ms;
|
||||
|
@ -22,12 +22,18 @@ SET(EffectEngineSOURCES
|
||||
${CURRENT_SOURCE_DIR}/Effect.cpp
|
||||
)
|
||||
|
||||
|
||||
set(EffectEngine_RESOURCES ${CURRENT_SOURCE_DIR}/EffectEngine.qrc)
|
||||
|
||||
QT4_WRAP_CPP(EffectEngineHEADERS_MOC ${EffectEngineQT_HEADERS})
|
||||
|
||||
qt4_add_resources(EffectEngine_RESOURCES_RCC ${EffectEngine_RESOURCES} OPTIONS "-no-compress")
|
||||
|
||||
add_library(effectengine
|
||||
${EffectEngineHEADERS}
|
||||
${EffectEngineQT_HEADERS}
|
||||
${EffectEngineHEADERS_MOC}
|
||||
${EffectEngine_RESOURCES_RCC}
|
||||
${EffectEngineSOURCES}
|
||||
)
|
||||
|
||||
|
342
libsrc/effectengine/EffectDefinition.schema.json
Normal file
342
libsrc/effectengine/EffectDefinition.schema.json
Normal file
@ -0,0 +1,342 @@
|
||||
{
|
||||
"type" : "object",
|
||||
"required" : true,
|
||||
"properties" : {
|
||||
"device" : {
|
||||
"type" : "object",
|
||||
"required" : true,
|
||||
"properties" : {
|
||||
"name" : {
|
||||
"type" : "string",
|
||||
"required" : true
|
||||
},
|
||||
"type" : {
|
||||
"type" : "string",
|
||||
"required" : true
|
||||
},
|
||||
"output" : {
|
||||
"type" : "string",
|
||||
"required" : true
|
||||
},
|
||||
"rate" : {
|
||||
"type" : "integer",
|
||||
"required" : true,
|
||||
"minimum" : 0
|
||||
},
|
||||
"colorOrder" : {
|
||||
"type" : "string",
|
||||
"required" : false
|
||||
},
|
||||
"bgr-output" : { // deprecated
|
||||
"type" : "boolean",
|
||||
"required" : false
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
},
|
||||
"color": {
|
||||
"type":"object",
|
||||
"required":false,
|
||||
"properties": {
|
||||
"hsv" : {
|
||||
"type" : "object",
|
||||
"required" : false,
|
||||
"properties" : {
|
||||
"saturationGain" : {
|
||||
"type" : "number",
|
||||
"required" : false,
|
||||
"minimum" : 0.0
|
||||
},
|
||||
"valueGain" : {
|
||||
"type" : "number",
|
||||
"required" : false,
|
||||
"minimum" : 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
},
|
||||
"red": {
|
||||
"type":"object",
|
||||
"required":false,
|
||||
"properties":{
|
||||
"gamma": {
|
||||
"type":"number",
|
||||
"required":false
|
||||
},
|
||||
"blacklevel": {
|
||||
"type":"number",
|
||||
"required":false
|
||||
},
|
||||
"whitelevel": {
|
||||
"type":"number",
|
||||
"required":false
|
||||
},
|
||||
"threshold": {
|
||||
"type":"number",
|
||||
"required":false,
|
||||
"minimum" : 0.0,
|
||||
"maximum" : 1.0
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
},
|
||||
"green": {
|
||||
"type":"object",
|
||||
"required":false,
|
||||
"properties":{
|
||||
"gamma": {
|
||||
"type":"number",
|
||||
"required":false
|
||||
},
|
||||
"blacklevel": {
|
||||
"type":"number",
|
||||
"required":false
|
||||
},
|
||||
"whitelevel": {
|
||||
"type":"number",
|
||||
"required":false
|
||||
},
|
||||
"threshold": {
|
||||
"type":"number",
|
||||
"required":false,
|
||||
"minimum" : 0.0,
|
||||
"maximum" : 1.0
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
},
|
||||
"blue": {
|
||||
"type":"object",
|
||||
"required":false,
|
||||
"properties":{
|
||||
"gamma": {
|
||||
"type":"number",
|
||||
"required":false
|
||||
},
|
||||
"whitelevel": {
|
||||
"type":"number",
|
||||
"required":false
|
||||
},
|
||||
"blacklevel": {
|
||||
"type":"number",
|
||||
"required":false
|
||||
},
|
||||
"threshold": {
|
||||
"type":"number",
|
||||
"required":false,
|
||||
"minimum" : 0.0,
|
||||
"maximum" : 1.0
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
},
|
||||
"smoothing" : {
|
||||
"type" : "object",
|
||||
"required" : false,
|
||||
"properties" : {
|
||||
"type" : {
|
||||
"type" : "enum",
|
||||
"required" : true,
|
||||
"values" : ["none", "linear"]
|
||||
},
|
||||
"time_ms" : {
|
||||
"type" : "integer",
|
||||
"required" : false,
|
||||
"minimum" : 10
|
||||
},
|
||||
"updateFrequency" : {
|
||||
"type" : "number",
|
||||
"required" : false,
|
||||
"minimum" : 0.001
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
}
|
||||
|
||||
},
|
||||
"additionalProperties" : false
|
||||
},
|
||||
"leds": {
|
||||
"type":"array",
|
||||
"required":true,
|
||||
"items": {
|
||||
"type":"object",
|
||||
"properties": {
|
||||
"index": {
|
||||
"type":"integer",
|
||||
"required":true
|
||||
},
|
||||
"hscan": {
|
||||
"type":"object",
|
||||
"required":true,
|
||||
"properties": {
|
||||
"minimum": {
|
||||
"type":"number",
|
||||
"required":true
|
||||
},
|
||||
"maximum": {
|
||||
"type":"number",
|
||||
"required":true
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
},
|
||||
"vscan": {
|
||||
"type":"object",
|
||||
"required":true,
|
||||
"properties": {
|
||||
"minimum": {
|
||||
"type":"number",
|
||||
"required":true
|
||||
},
|
||||
"maximum": {
|
||||
"type":"number",
|
||||
"required":true
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
}
|
||||
},
|
||||
"effects" :
|
||||
{
|
||||
"type" : "object",
|
||||
"required" : false,
|
||||
"properties" : {
|
||||
"paths" : {
|
||||
"type" : "array",
|
||||
"required" : false,
|
||||
"items" : {
|
||||
"type" : "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
},
|
||||
"blackborderdetector" :
|
||||
{
|
||||
"type" : "object",
|
||||
"required" : false,
|
||||
"properties" : {
|
||||
"enable" : {
|
||||
"type" : "boolean",
|
||||
"required" : true
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
},
|
||||
"xbmcVideoChecker" :
|
||||
{
|
||||
"type" : "object",
|
||||
"required" : false,
|
||||
"properties" : {
|
||||
"xbmcAddress" : {
|
||||
"type" : "string",
|
||||
"required" : true
|
||||
},
|
||||
"xbmcTcpPort" : {
|
||||
"type" : "integer",
|
||||
"required" : true
|
||||
},
|
||||
"grabVideo" : {
|
||||
"type" : "boolean",
|
||||
"required" : true
|
||||
},
|
||||
"grabPictures" : {
|
||||
"type" : "boolean",
|
||||
"required" : true
|
||||
},
|
||||
"grabAudio" : {
|
||||
"type" : "boolean",
|
||||
"required" : true
|
||||
},
|
||||
"grabMenu" : {
|
||||
"type" : "boolean",
|
||||
"required" : true
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
},
|
||||
"bootsequence" :
|
||||
{
|
||||
"type" : "object",
|
||||
"required" : false,
|
||||
"properties" : {
|
||||
"path" : {
|
||||
"type" : "string",
|
||||
"required" : true
|
||||
},
|
||||
"effect" : {
|
||||
"type" : "string",
|
||||
"required" : true
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
},
|
||||
"framegrabber" :
|
||||
{
|
||||
"type" : "object",
|
||||
"required" : false,
|
||||
"properties" : {
|
||||
"width" : {
|
||||
"type" : "integer",
|
||||
"required" : true
|
||||
},
|
||||
"height" : {
|
||||
"type" : "integer",
|
||||
"required" : true
|
||||
},
|
||||
"frequency_Hz" : {
|
||||
"type" : "integer",
|
||||
"required" : true
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
},
|
||||
"jsonServer" :
|
||||
{
|
||||
"type" : "object",
|
||||
"required" : false,
|
||||
"properties" : {
|
||||
"port" : {
|
||||
"type" : "integer",
|
||||
"required" : true,
|
||||
"minimum" : 0,
|
||||
"maximum" : 65535
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
},
|
||||
"protoServer" :
|
||||
{
|
||||
"type" : "object",
|
||||
"required" : false,
|
||||
"properties" : {
|
||||
"port" : {
|
||||
"type" : "integer",
|
||||
"required" : true,
|
||||
"minimum" : 0,
|
||||
"maximum" : 65535
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
},
|
||||
"boblightServer" :
|
||||
{
|
||||
"type" : "object",
|
||||
"required" : false,
|
||||
"properties" : {
|
||||
"port" : {
|
||||
"type" : "integer",
|
||||
"required" : true,
|
||||
"minimum" : 0,
|
||||
"maximum" : 65535
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
}
|
@ -1,8 +1,17 @@
|
||||
// Python includes
|
||||
#include <Python.h>
|
||||
|
||||
// Stl includes
|
||||
#include <fstream>
|
||||
|
||||
// Qt includes
|
||||
#include <QResource>
|
||||
#include <QMetaType>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
|
||||
// hyperion util includes
|
||||
#include <utils/jsonschema/JsonSchemaChecker.h>
|
||||
|
||||
// effect engine includes
|
||||
#include <effectengine/EffectEngine.h>
|
||||
@ -21,11 +30,26 @@ EffectEngine::EffectEngine(Hyperion * hyperion, const Json::Value & jsonEffectCo
|
||||
connect(_hyperion, SIGNAL(allChannelsCleared()), this, SLOT(allChannelsCleared()));
|
||||
|
||||
// read all effects
|
||||
std::vector<std::string> effectNames = jsonEffectConfig.getMemberNames();
|
||||
for (const std::string & name : effectNames)
|
||||
const Json::Value & paths = jsonEffectConfig["paths"];
|
||||
for (Json::UInt i = 0; i < paths.size(); ++i)
|
||||
{
|
||||
const Json::Value & info = jsonEffectConfig[name];
|
||||
_availableEffects.push_back({name, info["script"].asString(), info["args"]});
|
||||
const std::string & path = paths[i].asString();
|
||||
QDir directory(QString::fromStdString(path));
|
||||
if (!directory.exists())
|
||||
{
|
||||
std::cerr << "Effect directory can not be loaded: " << path << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
QStringList filenames = directory.entryList(QStringList() << "*.json", QDir::Files, QDir::Name | QDir::IgnoreCase);
|
||||
foreach (const QString & filename, filenames)
|
||||
{
|
||||
EffectDefinition def;
|
||||
if (loadEffectDefinition(path, filename.toStdString(), def))
|
||||
{
|
||||
_availableEffects.push_back(def);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// initialize the python interpreter
|
||||
@ -48,6 +72,51 @@ const std::list<EffectDefinition> &EffectEngine::getEffects() const
|
||||
return _availableEffects;
|
||||
}
|
||||
|
||||
bool EffectEngine::loadEffectDefinition(const std::string &path, const std::string &effectConfigFile, EffectDefinition & effectDefinition)
|
||||
{
|
||||
std::string fileName = path + QDir::separator().toAscii() + effectConfigFile;
|
||||
std::ifstream file(fileName.c_str());
|
||||
|
||||
if (!file.is_open())
|
||||
{
|
||||
std::cerr << "Effect file '" << fileName << "' could not be loaded" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the json config file
|
||||
Json::Reader jsonReader;
|
||||
Json::Value config;
|
||||
if (!jsonReader.parse(file, config, false))
|
||||
{
|
||||
std::cerr << "Error while reading effect '" << fileName << "': " << jsonReader.getFormattedErrorMessages() << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the json schema file
|
||||
QResource schemaData(":effect-schema");
|
||||
JsonSchemaChecker schemaChecker;
|
||||
Json::Value schema;
|
||||
Json::Reader().parse(reinterpret_cast<const char *>(schemaData.data()), reinterpret_cast<const char *>(schemaData.data()) + schemaData.size(), schema, false);
|
||||
schemaChecker.setSchema(schema);
|
||||
if (!schemaChecker.validate(config))
|
||||
{
|
||||
const std::list<std::string> & errors = schemaChecker.getMessages();
|
||||
foreach (const std::string & error, errors) {
|
||||
std::cerr << "Error while checking '" << fileName << "':" << error << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// setup the definition
|
||||
effectDefinition.name = config["name"].asString();
|
||||
effectDefinition.script = path + QDir::separator().toAscii() + config["script"].asString();
|
||||
effectDefinition.args = config["args"];
|
||||
|
||||
// return succes
|
||||
std::cout << "Effect loaded: " + effectDefinition.name << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
int EffectEngine::runEffect(const std::string &effectName, int priority, int timeout)
|
||||
{
|
||||
return runEffect(effectName, Json::Value(Json::nullValue), priority, timeout);
|
||||
|
5
libsrc/effectengine/EffectEngine.qrc
Normal file
5
libsrc/effectengine/EffectEngine.qrc
Normal file
@ -0,0 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file alias="effect-schema">EffectDefinition.schema.json</file>
|
||||
</qresource>
|
||||
</RCC>
|
@ -204,22 +204,16 @@
|
||||
{
|
||||
"type" : "object",
|
||||
"required" : false,
|
||||
"additionalProperties" :
|
||||
{
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"paths" : {
|
||||
"type" : "array",
|
||||
"required" : false,
|
||||
"properties" :
|
||||
{
|
||||
"script" : {
|
||||
"type" : "string",
|
||||
"required" : true
|
||||
"items" : {
|
||||
"type" : "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"args" : {
|
||||
"type" : "object",
|
||||
"required" : false
|
||||
}
|
||||
}
|
||||
}
|
||||
"additionalProperties" : false
|
||||
},
|
||||
"blackborderdetector" :
|
||||
{
|
||||
@ -270,17 +264,13 @@
|
||||
"type" : "object",
|
||||
"required" : false,
|
||||
"properties" : {
|
||||
"duration_ms" : {
|
||||
"type" : "integer",
|
||||
"required" : true
|
||||
},
|
||||
"script" : {
|
||||
"path" : {
|
||||
"type" : "string",
|
||||
"required" : true
|
||||
},
|
||||
"args" : {
|
||||
"type" : "object",
|
||||
"required" : false
|
||||
"effect" : {
|
||||
"type" : "string",
|
||||
"required" : true
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
|
Loading…
Reference in New Issue
Block a user