@p.buehler yes, I can indeed see them. However I didn’t get any notification about them. And ‘unfortunately’ they’re no more necessary.
Big news. So the last update was supposed to get the script working within its old limitations. Besides the fact that it still failed on some numbers according to @p.buehler, those limitations were pretty severe:
- attributes must be added for every component, creating many duplicates.
- attribute interpretation (replace
dez = [0 or 1]
by [‘horizontal’ or ‘vertical’]) was completely hardcoded and pretty much impossible to extend to all component type and attribute combinations.
- No possibility to respect ‘invalid’ component attributes. If you change a page
sta
from picture background to solid color, the pic
attribute disappears in the editor but it is still in the file, even though its value is meaningless. No realistic way to respect this in the old parser
- probably many other limitations I forgot.
So I took the only possible decision: write the component parser from scratch (almost). The result is a parser that has virtually no limitations anymore.
However, the old dictionary that listed every component with its attributes is gone. There is now a dictionary with ‘only’ the atteibutes. This dictionary is still far from being complete - especially with regards to all the attributes for the Intelligent series. To make the script already usable, you can add command line flags to include/exclude properties that the script doesn’t understand yet.
Speaking of command line options: some news here, too!
- Optionally generate a stats file that includes the code line counts from the command line output.
- Optionally generate a json filefor each parsed page
- Optionally specify a custom dictionary for parsing that extends or replaces the build-in ones.
Another thing that was missing until now: the Program.s code is now parsed, too.
For now this exciting new stuff lives in its own branch on GitHub: https://github.com/MMMZZZZ/Nextion2Text/tree/final-parser
Finally, for those interested in the more technical details, here’s how the new parser works (and how to write your own dictionary for it):
- Instead of having a dictionary of all component types with all their associated attributes (component page: id, vscope, sta, …) , there’s now a dictionary that contains all the attributes 'directly’. This allows much easier parsing since the ‘type’ attribute has no special role anymore.
- Parsing is done in multiple levels:
- A first low-level parsing extracts all the raw attributes and their data.
- Second step is to check if the parser knows the attribute structure (whether its a string or an integer). If so it converts the byte array accordingly, otherwise it remains a byte array.
- The third step is interpretation. This is where the new parser really shines. By now the values of all known attributes are already known, meaning they can be used to determine the meaning of other attributes. All those dependencies are included in the attributes dictionary (more about this below). Therefore the parser first resolves all the dependencies for this attribute and then, using the resulting dictionary, interpretes the attribute value.
- The structure of the attributes dictionary is not too complicated but very powerful.
- At the top level we have an entry for every known attribute (f.ex.
sta
, val
, …)
- Each attribute must have a
"struct"
entry that specifies whether it’s a string ("s"
) or an integer ("i"
).
- Any attribute can have any of the following optional entries:
-
"name": "description"
: a more descriptive expression than f.ex. vvs1
-
"mapping": dict
: replace attribute values by those specified here. F.ex. the mapping for the dez
property of a progress bar is "mapping: {0: "Horizontal", 1: "Vertical"}
.
-
"vis": bool
: whether it is a visual property or not.
-
"ignore": bool
whether to ignore (exclude/skip) this attribute during interpretation. If true it will not appear in the json or text files.
- Now the fun part. Any of the optional attributes can be wrapped into a dependency - which can be arbitrarily nested! How do you specify a dependency? Simply add an entry with the attribute name it depends on! Dependencies are resolved from top level down. So the entries of an attribute are scanned for dependency entries. If there are any, they get resolved. This gets repeated until there are no more such entries. So if one dependency introduces new ones, they get resolved, too. It also means, that nested dependencies overwrite ‘parent’ dependencies. Which makes sense becausw the latter ones are forcibly more specific.
- There is one additional dependency that is not an attribute but works just like the other ones:
"model": "T" / "K" / "P"
. Some attributes are only available on some models or have model-dependant meanings. Note that TJC X3/X5 series and Nextion Intelligent/P series are all included in "model": "P"
.
Sounds complicated? Simple example: A variable object has the following attributes: type
, id
, vscope
, sta
, val
, txt
, txt_maxl
. Depending on the value of sta
it‘s either a text variable or an integer variable. This means sta
determines whether we want to parse val
or txt
and txt_maxl
. Only sta
? No! (Btw here’s a cookie for you if you’re still reading. I really appreciate your interest!) Of course this rule only applies if the component is actually a variable (type=52
). Why? Take a number object: it also has a sta
attribute but it controls the numbers background, not its value. So let’s look at the dictionary entry for the val
attribute:
"val": {
"struct": "i",
"name": "Value",
"type": { # Dependency on type
52: { # In case type equals 52, use these entries
"sta": { # Dependency on sta
0: { # In case sta equals 0, use these entries
"ignore": False,
},
1: { # In case sta equals 1, use these entries
"ignore": True,
},
},
},
},
}
I hope this makes the basic concepts understandable. And if so, I’d really ap’reciate any contribution to the existing attributes dictionary!
Kind regards!
Max