openhab-core
openhab-core copied to clipboard
RFC: YAML rule (template) configuration
This is a "branch off" of #3666 for discussing the subtopic of rules and rule templates in the context of the new YAML based format.
Here is my suggestion for the rules structure, copied from #3666:
rules:
<uid>:
name: <rule name/label>
description: <description>
triggers:
<trigger ID>:
label: <trigger label>
description: <trigger description>
type: <trigger/module type>
config:
<parameter>: <value>
<parameter 2>: <value 2>
...
<trigger ID 2>:
...
actions:
<action ID>:
label: <action label>
description: <action description>
type: <action/module type>
config:
<parameter>: <value>
<parameter 2>: <value 2>
...
inputs:
<input>: <value>
<input 2>: <value 2>
...
<action ID 2>:
...
conditions:
<condition ID>:
label: <condition label>
description: <condition description>
type: <condition/module type>
config:
<parameter>: <value>
<parameter 2>: <value 2>
...
inputs:
<input>: <value>
<input 2>: <value 2>
...
<condition ID 2>:
...
tags:
- <tag 1>
- <tag 2>
...
visibility: <visible|expert|hidden>
templateUid:
config:
<parameter>: <value>
<parameter 2>: <value 2>
...
configDescription:
- <ConfigDescriptionParameter "flat mapped">
...
Some comments to the above:
visibilitydoesn't seem to be respected by MainUI, so maybe it has fallen out of use. It's still there in Core, and should probably still be possible to specify.templateUidand below are just needed for template based rules and rule stubs.configDescriptionis untested by me. I need to study exactly how it would be parsed as it is. SinceConfigDescriptionParametersare used many places in OH, it might be beneficial if this was separated out so that it could be reused for other "elements".
Rules DSL code:
rule "mode TV"
when
Item TvPower changed from OFF to ON
then
logInfo("Rule","mode TV")
// STOP Sonos salon
SonosSalonStop.sendCommand(ON)
end
would be represented as
version: 2
rules:
mode-tv-yaml:
name: mode TV
triggers:
- config:
itemName: TvPower
state: ON
previousState: OFF
type: core.ItemStateChangeTrigger
actions:
script:
config:
type: application/vnd.openhab.dsl.rule
script: |
logInfo("Rule","mode TV")
// STOP Sonos salon
SonosSalonStop.sendCommand(ON)
type: script.ScriptAction
It is "more verbose", but it's also infinitely more flexible. A Rules DSL rule can only contain an arbitrary number of triggers and one action. This format can contain an arbitrary number of triggers, conditions and actions, and the actions can be of any type (built-in or scripted). In addition, it supports rule templates, something that is basically "impossible" to do with Rules DSL without a huge redesign of the Rules DSL syntax.
configDescription is untested by me. I need to study exactly how it would be parsed as it is. Since ConfigDescriptionParameters are used many places in OH, it might be beneficial if this was separated out so that it could be reused for other "elements".
If I understand correctly, config is what a rule template author would use to define the parameters in use by the template. configDescription is used by template users to supply values for those properties when instantiating a rule from a template. Correct?
I'm just trying to make sure I understand.
As to the format, is there a way to define a shorthand version of the format?
As I see it, the most common cases for triggers will only use the config section and suspect that to be the case for most actions and conditions also. It feels awkward to require every time, even if all that is there is the config.
Maybe something like:
version: 2
rules:
mode-tv-yaml:
name: mode TV
triggers:
- ItemStateChange:
item: TvPower
state: ON
previousState: OFF
actions:
- scriptAction:
type: DSL # use the same string used when employing a SCRIPT transformation
script: |
logInfo("Rule","mode TV")
// STOP Sonos salon
SonosSalonStop.sendCommand(ON)
Obviously if one had inputs or want to define names and descriptions and such the full format would be used. But it would be much easier for end users still if we can capture some of the stuff that is requires in a more terse way.
These are just half-baked ideas but thoughts that occur when looking at the format and all the redundancy that is present for a simple rule. It seems we have opportunities to present the end user something a little better.
If I understand correctly,
configis what a rule template author would use to define the parameters in use by the template.configDescriptionis used by template users to supply values for those properties when instantiating a rule from a template. Correct?
No, it's the other way around. configDescriptions turns into Java ConfigDescriptionParameter Java instances, and they are the ones that hold the definition of the template parameters. config is what a "rule stub" populates, the configured values of the defined parameters
As to the format, is there a way to define a shorthand version of the format?
In general, I'd say that there's very little we can't do. The question should probably rather be if we want to.
I know that there's some kind of consensus that the format should be "uncoupled" from the Java code, and only cater to the users. Personally, I'm not convinced this is a good idea in general, but that it might be appropriate in specific situations. Everything that maps can be converted "automatically" by existing libraries like Jackson. No manual intervention is required. Everything that differs, must have code that handles this. Such code usually imposes some kind of restrictions/constraints on the overall logic, so that one "burns some bridges" every time one makes "special handling". It doesn't take too long to paint oneself into a corner, where the workarounds to implement new things or accommodate "external" changes grows exponentially larger until it ends up being "impossible", or at least so much work that nobody is willing to take it on.
That's when "systems" die and people define new ones, just to make many of the mistakes that ware already solved by the "old system". And then we rinse and repeat this process indefinitely.
I think things can be given a much longer life if some care is taken early in the design phase, and if established principles are respected through its lifetime.
Because of how I "understand" the lifecycle of such systems, I think the correct thing is to weigh carefully for and against any "customization". If a customization brings great benefit but very few restrictions, it should absolutely be done. If a customization brings minimal or minor benefit but significant restrictions, it probably should not be done, in my opinion.
All that said, I'm not sure what you mean by "format" here. Are you thinking of type? It is of course possible to make a mapping for types, but that mapping must be maintained so that somebody remembers to add a new mapping there if a new "type" is created. Also, add-ons can usually inject such things dynamically, so you can never get that mapping to be complete.
We could make it a "map" where the type is the key, but there's a caveat. I've already done quite some "trickery" there, because there's also an ID that is required for each module. The way I've currently implemented it, it is automatically generated if omitted. But, if you want to duplicate a rule completely, there must be a way to specify the ID. The result can be seen in this comment. Also handling the ID would further complicate "defining" the type as a map key.
I can't give a more specific answer until I've tried to implement it, as that's when you discover all the "real problems". But, my current implementation for module ID is already "pushing" the current "YAML parser structure" quite a bit, and I'm not really happy with the technical solution I had to make.
If we want lots of such adaptations, I think the fundamental "YAML parser structure" should be redesigned so that this kind of logic can be made in a standardized, less "hacky", way.
An alternative to making mappings to making it easier to remember, is to provide "intellisense"/autocompletion. I saw that @jimtng had made some pretty nice autocompletion for things in #3180. A similar solution for rules, in addition to adding the same capabilities to OH's vscode extension, might go a long way in making such a mapping unnecessary.
When it comes to imposing extra work on the end users and imposing some extra work on the developers, the end users should usually come out ahead. Anything imposed on them is going to have a much larger overall impact.
The way I've currently implemented it, it is automatically generated if omitted. But, if you want to duplicate a rule completely, there must be a way to specify the ID.
The ID is not something that end users need to care about. It should be autogenerated. There is no practical reason why the end users need to specify these and today they are generated automatically.
The "core." part of "core.ItemStateChangeTrigger" is meaningless to the end users and the "Trigger" part is redundant.
Why does every trigger, action and condition have to have a config and it isn't until we get to the type under that config that we even know what it is? That adds extra nesting which adds additional cognitive load.
Those are the sorts of things that come to my mind.
But I'm not going to get into a 100 post long argument about it. Something is better than nothing. I just think it would be a missed opportunity to create something that's easy to code at the expense of all the humans who actually have to write in it. It gets the priorities backwards. Every effort should be made to make sure the file format is easy for people to read, understand, and write.
When it comes to imposing extra work on the end users and imposing some extra work on the developers, the end users should usually come out ahead. Anything imposed on them is going to have a much larger overall impact.
It's not about "some extra work on the developers", it's about making solutions that end up being so complex that they become "impossible" to make reliable or maintain. And this isn't a comment on this particular "feature", it's on the general principle.
Why is DSL being "replaced" now? Why is there an effort to make something new? For those very reasons I'm trying to shed light on: My guess is that it's a combination of past decisions that have left little flexibility for further evolution without breaking what's there, and that the complexity is high enough that few will touch it.
All I'm asking for is some balance, don't make things more complicated than they need to be.
The ID is not something that end users need to care about. It should be autogenerated. There is no practical reason why the end users need to specify these and today they are generated automatically.
That's my assumption too, usually, but I can't say for sure that it never has any significance, so I'm not comfortable completely removing the possibility to specify it. Maybe I'm worried about nothing, I just don't have the necessary overview to be certain. If others can reassure me that it will never be needed, then that's fine. Note that currently, there is a "bug" in Core, which I have fixed in #4633, which means that module IDs must be unique across "module types" (triggers, conditions and actions). I assume that the current autogeneration you're referring to is when you create a rule through the UI, I haven't looked at the logic used there, but I assume that it makes sure that the generated IDs are unique "globally" in that rule. That's not possible with the current "parser logic", since the generating code don't know about the other "module types", so I had to make the fix to make autogeneration work at all.
The "core." part of "core.ItemStateChangeTrigger" is meaningless to the end users and the "Trigger" part is redundant.
Sure, and I don't have all the answers to this. I can just say that if a mapping is to be made, it must be possible to define some logic that will make this consistently, in all situations. A "manual mapping" doesn't seem like a viable option since add-ons, including bindings, add these dynamically. Check out the API explorer under /rest/module-types to see what's available in a given system. I see that I have binding generated types that's pretty hard to "figure out", like milllanpanel.setCustomName#42c25be3a5db3f1f8377f4c2db62bfe9 in my dev installation.
An autocomplete functionality might be able to query the system to know exactly which types are available. The closest thing to a mapping that could be applied by the parser that I can think of is that we defined "short forms" of the most common types that were hardcoded in the parser to translate to the "full ID". If no match is found among the "short forms", the code will be used literally, so that you can also specify the "full ID".
Why does every trigger, action and condition have to have a config and it isn't until we get to the type under that config that we even know what it is? That adds extra nesting which adds additional cognitive load.
The underlying structure is what it is. So, all we can do here is to try to hide the details that we think are "in the way". The config (or actually configuration) field seems to be a "general structure" found many, many places around OH. There is "helper code" in Core that helps utilize this in a standardized way, benefitting e.g. bindings that don't have to "reinvent the wheel". So, we shouldn't question why it's there, but rather if we can make it easier to handle in some way that is manageable.
The "basic structure" of a module is this:
/**
* Id of the ModuleImpl. It is mandatory and unique identifier in scope of the {@link Rule}. The id of the
* {@link ModuleImpl} is used to identify the module
* in the {@link Rule}.
*/
private String id;
/**
* The label is a short, user friendly name of the {@link ModuleImpl} defined by
* this descriptor.
*/
private @Nullable String label;
/**
* The description is a long, user friendly description of the {@link ModuleImpl} defined by this descriptor.
*/
private @Nullable String description;
/**
* Configuration values of the ModuleImpl.
*
* @see {@link ConfigDescriptionParameter}.
*/
private Configuration configuration;
/**
* Unique type id of this module.
*/
private String typeUID;
typeUID is what is represented as type in the DTO, to make it less cryptic/hard to type. Module implementations can add additional fields, like input to this. The reason the config element is there is because of this structure. Configuration is by definition a map of string keys and object values. It is a container, and in fact the exact "same" container that you find for e.g a Thing. So, the config element for a module is identical in structure to the config element for a rule or a thing. That in itself has some value for a user I guess, that the structure is identical and thus familiar after a while.
Your argument seems to be that "usually we don't need any of the other fields, so let's just assume that what's on the module level is the configuration parameters". I can agree that this will probably often be the case, but we would need to find a logic that would remove all ambiguity so that the parser could "know" how to interpret it. Because, it's obvious that you would need to retain the possibility also specify the "full version", in case you want a name, a description or one or more inputs.
Ideas are appreciated, I'm not sure if I can see exactly how to achieve this unambiguity, since, we can't assume anything of neither the names of fields for Module implementations nor for the name of configuration parameters.
But I'm not going to get into a 100 post long argument about it. Something is better than nothing. I just think it would be a missed opportunity to create something that's easy to code at the expense of all the humans who actually have to write in it. It gets the priorities backwards. Every effort should be made to make sure the file format is easy for people to read, understand, and write.
I'm not sure why you take this "stance" - I'm trying to be flexible and open to suggestions. But, I must be allowed to present my view as well, in addition to any limitations that I don't know how to get around. It's absolutely not about making something that is "easy to code". It's about making something that is manageable, that isn't so ambitious that you'd need a team of paid professionals working full-time to maintain it.
People are probably different, but I personally never tries to "learn" such structures anyway. I will always use something as a template and then modify that - so I get a lot of the "skeleton" for free. There are just too many things to remember, and life is too short, for me to try to stuff all this into my head 😉
Regardless of this particular issue with the module types, I think it's clear that the "YAML module parser" design is too simple as it is now. That's why I made a "hack" to be able to dynamically interpret the module IDs. In short, the way it is now, it doesn't really allow for any "dynamic interpretation" at all. The file must fit 100% to what's specified in the DTO, so things can only be structured in one way.
Redesigning this to be more flexible is possible, but I'm afraid it would delay everything. Given "the race" to wrap things up for 5.0, this might not be a great time to do that. At the same time, not doing it restricts that's possible. What's problematic is always "taking things away", not "adding new possibilities", so I guess supplying a less flexible structure now that is revisited and made more dynamic/smarter later might be the best option.
version: 2 rules: mode-tv-yaml: name: mode TV triggers: - ItemStateChange: item: TvPower state: ON previousState: OFF actions: - scriptAction: type: DSL # use the same string used when employing a SCRIPT transformation script: | logInfo("Rule","mode TV") // STOP Sonos salon SonosSalonStop.sendCommand(ON)
I like this a lot. The only change I can think of is
triggers:
- type: ItemStateChange
...
actions:
- type: scriptAction
scriptType: DSL # use the same string used when employing a SCRIPT transformation
or:
- scriptAction: DSL
The "core." part of "core.ItemStateChangeTrigger" is meaningless to the end users and the "Trigger" part is redundant.
+1000
As to the format, is there a way to define a shorthand version of the format?
In general, I'd say that there's very little we can't do. The question should probably rather be if we want to.
I know that there's some kind of consensus that the format should be "uncoupled" from the Java code, and only cater to the users
I agree with @rkoshak, the syntax and key names should be made easy for the users to read and write. If necessary, rename the java DTO to keep them in sync, if that's easy enough to do.
The whole and only purpose of the format is for humans to write and read, not for some internal storage that only gets read/written to by code - we already have the internal jsondb for that.
I agree with @rkoshak, the syntax and key names should be made easy for the users to read and write. If necessary, rename the java DTO to keep them in sync, if that's easy enough to do.
The whole and only purpose of the format is for humans to write and read, not for some internal storage that only gets read/written to by code - we already have the internal jsondb for that.
I'd appreciate it if what I tried to explain was read. It's not about renaming a DTO or fields in a DTO. I have explained the options as I see them. The type field has a string value, all these "core.xxx" UIDs are hardcoded all around the code. Check module types in the API explorer, and you will see that some of them even come from bindings.
The "core." part of "core.ItemStateChangeTrigger" is meaningless to the end users and the "Trigger" part is redundant.
+1000
OK, having thought about it again and re-read @nadahar's comment, I see that it's going to be tricky to remove the core. part because there are other triggers such as timer.xxxx (off the top of my head).
TL;DR: I'm fine either way, given the challenge involved, I can see the argument for keeping it as is for the trigger name.
We could do a "search" given ItemStateChange and look that up in all the trigger ids I suppose, but as @Nadahar pointed out, we'll have to start imposing a rule that with xxx.YyyTrigger, the Yyy part must be unique across all ids. I don't think there's currently such a rule, e.g. it's currently "allowed" to create mylibrary.ItemStateChangeTrigger
So one way to fix this, if we were to wipe the slate clean, would be to actually rename, in core code, core.ItemStateChangeTrigger to ItemStateChange, or ItemStateChangeTrigger (it is probably fair to say that ALL triggers must end with Trigger, therefore we can remove that in the YAML, and the code can re-add it back to resolve the real name). However, I haven't thought this through, there may be other issues in doing so?
But even adding that extra Trigger suffix would mean a special handling for triggers/type key. Is it worth it? Maybe, or maybe not.
What are the other issues?
So one way to fix this, if we were to wipe the slate clean, would be to actually rename, in core code,
core.ItemStateChangeTriggertoItemStateChange, orItemStateChangeTrigger(it is probably fair to say that ALL triggers must end with Trigger, therefore we can remove that in the YAML, and the code can re-add it back to resolve the real name). However, I haven't thought this through, there may be other issues in doing so
If we rename the existing typeUIDs, we will break all existing rules that use them.
As I see it, there are several potential approaches. As I mentioned before, create a "short code" for the most commonly used typeUIDs is one. We could also for example define that if it is "unqualified" (lacking e.g. the code. prefix), it would be interpreted as core.. But, not all of "the usual ones" are core., so I think the "short code" approach might be better.
Trickery like "assuming" that a trigger ends with Trigger can be done, but it will only work if we can guarantee that it's always the case. Again, add-ons can supply whatever codes they want here, so assumptions could easily go wrong.
The parser doesn't have any "overview" over what typeUIDs are valid at any given moment - it would be possible to make it look it up, but that would be a quite "bad hack" IMO, it would break separation between different modules. Also, that would have all kind of potential pitfalls, like whether or not the typeUID in question is registered at the exact moment the rule is parsed.
What are the other issues?
The biggest issue I see at the moment is how to eliminate config:. I can't see a way to infer whether the elements on the level given level are "config parameters" or "module parameters", because again, the names of these could be anything. If somebody has a bright idea for how to do that, I'm all ears.
I have an idea regarding config. If we can assert that a module will always have at least one configuration parameter, the logic could be: If there is an element called config, the rest of the elements at that level are "module parameters". If there are no config element, they are "configuration parameters". That might work, I just don't know if that assumption holds water.
create a "short code" for the most commonly used typeUIDs is one. We could also for example define that if it is "unqualified" (lacking e.g. the
code.prefix), it would be interpreted ascore.. But, not all of "the usual ones" arecore., so I think the "short code" approach might be better.
I like this idea, and furthermore, we don't even have to implement it right now, i.e. we can require the same old trigger IDs now, and introduce this short code later on and it will still work, right?
Or just leave it as is: easier, and cleaner in terms of code maintenance.
The biggest issue I see at the moment is how to eliminate
config:the rest of the elements at that level are "module parameters"
I'm being lazy - what are module parameters?
I like this idea, and furthermore, we don't even have to implement it right now, i.e. we can require the same old trigger IDs now, and introduce this short code later on and it will still work, right?
Sure. But we could also come up with the most obvious ones now, and then extend the list later.
I'm being lazy - what are module parameters?
They aren't actually called that, which is why I used double quotes. I was thinking of these fields that are on the "module level":
private String id;
private @Nullable String label;
private @Nullable String description;
private Configuration configuration;
private String typeUID;
In addition to these, there are fields defined by implementations of Module. Action and Condition define input, but add-ons can define anything they want.
@rkoshak argued that in most (simple) cases, if id is autogenerated and typeUID is used as a map key, label, description and input will often not be needed. In those cases, the config element is superfluous, and everything specified at that level should instead be interpreted as if they were "sub-elements" of config. That way, you would "erase" one level when it wasn't needed, but if you needed to use some of the other fields, you could opt to provide the full structure.
I'm not sure if it's easy to implement it, so this is just an idea: Given a list of keys, first try to match them against "module parameters" then apply the rest to configuration - or vice versa.
Do we have ConfigurationParameter for the module config? If we do, that can be used as an easy list to check against.
Sure. But we could also come up with the most obvious ones now, and then extend the list later.
Sounds good to me. Just off the top of my head:
Script:script.ScriptCondition(for condition, and for Action)ItemStateChanged:core.ItemStateChangeTriggerItemStateUpdated:core.ItemStateUpdateTriggerReceivedCommand,CommandReceived, orItemCommand?:core.ItemCommandTrigger(in triggers section)SendCommandorItemCommand:core.ItemCommandAction(when it's in theactions:section)
I presume we want TitleCase rather than snake_case?
About the structure, which do we want?
triggers:
- ItemStateChanged:
item: My_Item
state: ON
previousState: OFF
In @rkoshak's example, he puts the item, state, etc under the same indentation level as their key. Maybe that's allowed in YAML? but I think the generator would indent them, and that's also less confusing to me.
vs
triggers:
- type: ItemStateChanged
item: My_Item
state: ON
previousState: OFF
I typed my example on my phone and didn't notice the indentation. Don't read to much into it. What ever indentation makes sense and is valid yaml is what I intended.
I'm not sure if it's easy to implement it, so this is just an idea: Given a list of keys, first try to match them against "module parameters" then apply the rest to configuration - or vice versa.
The problem with this is that the parser has no idea what are valid "parameter names"/keys for either. The parser doesn't have any information about the class that implements Module that will be used, and even if it did, it would have to use reflection to enumerate the field names. I'm guessing that in instances where an add-on provides the Module implementation, the parser won't even have access to the class. When it comes to valid configuration "parameter names"/keys, I don't think there are any rules at all. They can be anything.
I think using the existence of a config element to decide "what level we're on" is the closest thing to a solution that could work, I just don't know if all Modules will have at least one configuration parameter or not.
Do we have ConfigurationParameter for the module config? If we do, that can be used as an easy list to check against.
No.
About the structure, which do we want?
I understand it as if there's a preference for the "map style" here, but it depends on the id parameter. In my current implementation, you can specify modules in 3 ways:
- As a map where the ID is the key.
- As an array where the ID is specified as a field, e.g
id: script. - As an array without an ID, in which case it will be autogenerated by starting at 1 and counting upwards until it find an ID not currently in use.
I'm not saying that's the way it has to be, but if we want to retain the possibility to specify the ID, it's impossible to use the type as a key (unless there's a way that I haven't thought of).
If we can be absolutely sure that the ID will never need to be specified, it can always be autogenerated and this "problem" isn't a factor. However, we should remember that this code won't only be used for reading rules from files, it will also be used for e.g. conversions. I feel that it would be safest to allow IDs to optionally be specified, so that the rule is "an exact copy" of the source.
I feel that it would be safest to allow IDs to optionally be specified, so that the rule is "an exact copy" of the source.
I agree.
So perhaps this can be either:
2.As an array where the ID is specified as a field, e.g id: script. 3. As an array without an ID, in which case it will be autogenerated by starting at 1 and counting upwards until it find an ID not currently in use
But, that means either of the above options I posted can still be used.
triggers:
- type: ItemStateChanged
item: My_Item
state: ON
previousState: OFF
id: xxx (optional)
triggers:
- ItemStateChanged:
item: My_Item
state: ON
previousState: OFF
id: xxxx (optional)
PS this is similar to the the current implementation (except right now id is probably mandatory)
The problem with this is that the parser has no idea what are valid "parameter names"/keys for either. The parser doesn't have any information about the class that implements
Modulethat will be used, and even if it did, it would have to use reflection to enumerate the field names. I'm guessing that in instances where an add-on provides theModuleimplementation, the parser won't even have access to the class. When it comes to valid configuration "parameter names"/keys, I don't think there are any rules at all. They can be anything.I think using the existence of a
configelement to decide "what level we're on" is the closest thing to a solution that could work, I just don't know if allModules will have at least one configuration parameter or not.
Given this, I'd rather just be consistent and keep the configuration: and not mix them up.
I forgot whether I was involved in the decision to shorten configuration: to config: but it seems that configuration: is the nicer one of the two, strictly speaking from "readability" POV.
But, that means either of the above options I posted can still be used.
No, you're "skipping a step". The ID is on the "module parameter" level, not the "configuration parameter" level. The "real" structure is this:
triggers:
- id: xxxx (optional)
type: ItemStateChanged
config:
item: My_Item
state: ON
previousState: OFF
or
triggers:
xxxx (id):
type: ItemStateChanged
config:
item: My_Item
state: ON
previousState: OFF
So, to be able to do the "remove config" trick, we can't have type on the same level. That means that type must be a key, but for that to be true, there is no place to put the ID.
Yup, I didn't incorporate the split back to a separate configuration section. So this is the one I think, which is basically the same as it is now, except the type short code and configuration -> config
triggers:
- id: xxxx (optional)
type: ItemStateChanged
config:
item: My_Item
state: ON
previousState: OFF
I forgot whether I was involved in the decision to shorten configuration: to config: but it seems that configuration: is the nicer one of the two, strictly speaking from "readability" POV.
I'm not sure if I understand you correctly or not here, but I'm trying: Shortening "configuration" to "config" was never really discussed for rules/modules specifically, I just assumed that it should be "config". The reason is that I saw that this was already done for Things, and as far as I remember, it's also what's used for UI widgets. Given that this is a "universal" format where you can mix different kind of elements/objects in the same file, I think it's important that it's coherent/consistent. Thus, I went with "config" from the start.
Personally, I prefer "configuration" because I don't think the 400 ms it takes to type "uration" is makes up for the loss in readability, especially if autocompletion is available. But, I'm usually "against the trend" on this, so I try to conform and do as others prefer. In this case, I think consistency trumps (not in a fascist sense) other considerations.
I'm questioning whether name (for the rule, not the module) should be called label in this format, since it seems label is what this field is called most other places. Although I personally think name is a better fit, consistency seems more important..?
oops wrong, rule name is not the uid... it is indeed the label, sorry!
It's one of those things...
rule name is its uid
No, it's not:
https://github.com/openhab/openhab-core/blob/c40c258e049710f246c86953b118f1ff1c2990f2/bundles/org.openhab.core.automation/src/main/java/org/openhab/core/automation/Rule.java#L47-L75
uid and name are separate fields, and only uid has the unique requirement. In MainUI today, the name field is called "Label".
Even more confusing, RuleTemplate has identical fields to Rule, except that name is called label. So, what I think has happened is that somebody has decided at one point that "label" is better, but for backwards compatibility, name in rules has been left as it was. Thus, when we make this new format, we should probably hide that and "pretend" that rules follow the convention.
For those unfamiliar with rule templates, I've made a template (with only a single variable for simplicity) based on the "mode TV" example rule used above:
version: 2
ruleTemplates:
mode-tv-yaml:
label: mode TV template
configDescriptions:
- name: sourceItem
type: TEXT
context: item
label: Source Item
description: The source Item whose state to monitor
required: true
triggers:
- config:
itemName: '{{sourceItem}}'
state: ON
previousState: OFF
type: core.ItemStateChangeTrigger
actions:
script:
config:
type: application/vnd.openhab.dsl.rule
script: |
logInfo("Rule","mode TV")
// STOP Sonos salon
SonosSalonStop.sendCommand(ON)
type: script.ScriptAction
As you can see, it's almost identical to the rule, the difference is that it has the configDescriptions element which describes the variable/parameter, and the itemName has the placeholder '{{sourceItem}}' that references the parameter. The placeholders can be placed mostly "anywhere" I think, like inside scripts etc.
The corresponding rule, what I call a "rule stub", is then extremely simple:
version: 2
rules:
name: Template based mode TV
templateUid: mode-tv-yaml
config:
sourceItem: 'TvPower'
These two together will create a rule that is identical to the original example, with the exception that it's easy to use the same logic/template for multiple Items (the example isn't well suited to be used as a template, since it's quite specific, but that's the example we already discussed).
I am unfamiliar with the whole marketplace rules / templates. How does this connect with the marketplace stuff, or is this a separate thing?
I am unfamiliar with the whole marketplace rules / templates. How does this connect with the marketplace stuff, or is this a separate thing?
The marketplace has its own parsers, although I've done some work to "unify" the marketplace parsing with the file parsing. I'm not sure if that has been merged or not, it was too long ago to remember the details. Only rule templates exist on the marketplace, since a rule is usually tied directly to existing items/things/rules/scripts in the existing installation. A rule template is what "generalizes" a rule so that it can be reused, which is what makes sense in the marketplace.
But, this must be handled too. I've worked quite a lot with the marketplace (in trying to implement versioned add-ons), so I'd probably be able to handle that relatively quickly, given that I find a way to do it.
As the "YAML parser" is now, it's directly tied to a file watcher and doesn't allow converting content from other sources, but I'm waiting to see what @lolodomo is doing to make the "format conversion" logic, because he'll face the same challenge there. My thought was that when the "conversion" is done, I can modify the marketplace parsers to use the same calls that the "conversion" does for parsing the marketplace content.