Text template plugin requirements

This is an offshoot from another thread, where a couple of us were talking about templating (that is, building strings from fixed bits and variable bits). It’s general enough that it could be useful to a number of developers, so I’m peeling it off into its own thread.

What we see a need for is the ability for Lua code to be able to get string values from the LuaUPnP process and format the string values into messages that, for instance, might be sent to users as a notification:

Security system: {PartitionName} was armed at {time} by {urn:micasaverde-com:serviceId:AlarmPartition2,LastUser}

or formatted into another plugin’s variables for display in the dashboard:

Last state change: {time} by {device}.

Right now I’m interested in the requirements of such a tool, what users of it would need from it. What variables would we want to substitute? How complex a templating language is reasonable (if/else? loops?)? How would developers want to invoke it (as a Lua library? through the registered-call mechanism?)

The aim is to have something that developers want to use. Simplicity is important.

Input from other developers of plugins welcome.

Thanks for this new thread, i think i’ll be helpfull for a lots of plugins.

I’ll probably start working on it during next weeks.

For the moment, I’d keep it to simple substitutions/expansions (no expressions, no conditionals), along with a pre-defined vocabulary of tokens… one that lets users reach out to [read] all the data in their Vera if needed.

If the EL is simple enough, it should be possible to add to it at a later date with more complex functionality.

My initial thinking was to have expressions like the following, given in the form of examples:

[ul][li][tt]device.name[/tt] - the title/label of the current device
[/li][li][tt]device.id[/tt] - the Id of the current device
[/li][li][tt]device.altid[/tt] - the altId (ZWave node Id, etc) of the device
[/li][li][tt]room.title[/tt] - the title/label of the current room (also available via [tt]device.room.title[/tt])
[/li][li][tt]system.latitude[/tt] - system properties including Lat/Long (etc) if they’re available to be read
[/li][li][tt]category.name[/tt] - the name of the current devices category
[/li][/ul]

things that are logically “list of” containers could also support an array-access syntax, such as the following example:

[ul][li][tt]device[45].name[/tt] - the title/label of device#45
[/li][li][tt]category[5].name[/tt] - the name/label of category#5
[/li][li][tt][tt]room[3].name[/tt] - the name/label of room#5
[/li][li][tt][tt]device[45].room.name[/tt] - the name/label of the room from device#45
[/li][li][tt][tt]device[45].category.name[/tt] - the name/label of the category for device#45
[/li][/ul]

For accessing Service variables, a full syntax could be something like:

[ul][li]device.service[urn:micasaverde-com:serviceId:SecuritySensor1].LastTrip
[/li][/ul]

which will get a little wordy, so we might want some simplifications/aliases, maybe like one of the following (for common stuff):

[ul][li][tt]device.service[SecuritySensor1].LastTrip[/tt] OR
[/li][li][tt]device.security.LastTrip[/tt] - for perhaps a handful of “special” services that are used all the time ([tt]security, switch, dimmer, energy, alarm[/tt] :wink: )
[/li][/ul]

A device would have the following “special” children:

[ul][li][tt]device.room.[/tt] - a reference to the room it’s in
[/li][li][tt]device.parent.[/tt] - for child devices like alarm sensors, this is the alarm panel or “nil” if a top-level device
[/li][li][tt]device.service.[/tt] - the array of services that the device implements
[/li][li][tt]device.category.[/tt] - a reference to the Category containing this device
[/li][/ul]

within the device, we’d come up with some sort of naming convention for the pre-defined attrs (things like [tt]id, name, altid, mac, model, device_file, time_created, category_num[/tt]).

We’d likely want to reserve other special-meaning children for each of the pre-defined tokens, so that we have space to expand the language at a later time (things like [tt].children[/tt], for the list of child objects, etc)

Later on, we can add the ability for any “miss” in the syntax to call the client/consumer so they can add extensions/tokens as needed. Initially I’d like to avoid that though to keep it simple.

For using the above in String substitutions, I’m not really fussed about the characters used. I do like using curly braces, since it’s consistent with Java-etc, but anything would do.

To see how the above might look, I mocked up a library ([tt]verael.lua[/tt]) and test driver ([tt]verael-test.lua[/tt]) to see how the above command structure might translate into the corresponding [tt]luup[/tt] commands.

I mocked up the [tt]luup[/tt] commands themselves so that I can see what commands it would run in various scenarios. Instead of running the commands, I print out the equivalent [tt]luup[/tt] command that would need to be [dynamically] run given the markup presented.

There are a few syntax examples in [tt]verael-test.lua[/tt], based upon the above outline, that we can use to evolve the discussion.

Here’s the input:

Values... {system.latitude} {system.la4-titude} {system.timezone} {system.city} {device.name} {device.ip} {device.mac} {device.parent.name} {device.service[urn:micasaverde-com:serviceId:SecuritySensor1].LastTrip} {device.service[].LastTrip} {device.room.name} {device.parent.room.name} Formatting... Number values we dont understand {5.5}, {5.5,Number}, {5.5,Number,%10.4f} Number values we understand {pi}, {pi,Number}, {pi,Number,%10.3f} String values we dont understand {title} Dates we understand {currentTime}, {currentTime,Date}, {currentTime,Date,%x}, {currentTime,Date,%H:%M}
and the corresponding output of the command it would run to get the value if run inside Vera:

Values... luup.latitude INVALID luup.timezone luup.city luup.attr_get("name",57) luup.attr_get("ip",57) luup.attr_get("mac",57) luup.attr_get("name",luup.devices[57].parent_num) luup.variable_get("urn:micasaverde-com:serviceId:SecuritySensor1","LastTrip",57) INVALID luup.rooms[luup.devices[57].room_num] luup.rooms[luup.devices[luup.devices[57].parent_num].room_num] Formatting... Number values we dont understand {5.5}, {5.5,Number}, {5.5,Number,%10.4f} Number values we understand 3.141592654, 3.141592654, 3.142 String values we dont understand {title} Dates we understand 1345431388, Sun Aug 19 19:56:28 2012, 08/19/12, 19:56

Does someone already know, or already try this library : http://leg.luaforge.net

It seem’s that it’s design for language parsing based on a grammar.

@guessed

I made a new version 0.2 of the library, with the following changes.

This is a test version for the moment that still need some huge works, for error handling …

[ul][li]Evaluation of parsed string[/li]
[li]We can now use variable with _ like ra_server[/li][/ul]

So the following code

el = require("verael")

result = el.resolve([[                                                                                                              

Date      : {currentTime,"%d.%m.%Y"}  
                                                                                                                                    
                                                                                                           
SYSTEM                                                                                                                              
Version   : {system.version_branch}.{system.version_major}.{system.version_minor}          
Serveur   : {system.ra_server}  
Longitude : {system.longitude}                                                                                                 
Latitude  : {system.latitude}                                                                                                       
Timezone  : {system.timezone}                                                                                                       
Ville     : {system.city}                                                                                                           
On Dasboard  : {device.onDashboard}
                                                                                                                                     
Name      : {device.name}                                                                                   
IP        : {device.ip}                                                                    
Mac       : {device.mac}                                                                                                       
Room      : {device.room}                                                                                                                                                            
Parent    : {device.id_parent} 

Device Volume : {device.service[urn:onkyo-com:serviceId:Receiver1].Volume}

]], 25)

Now give this result

Date      : 1345991816  


SYSTEM                                                                                                                              
Version   : 1.5.408          
Serveur   : fwd2.mios.com  
Longitude : 7.34486                                                                                                 
Latitude  : 31.8542                                                                                                       
Timezone  : 2                                                                                                       
Ville     : Paris                                                                                                           
On Dasboard  : 1

Name      : Onkyo TX-NR807                                                                                   
IP        : 192.168.1.206                                                                    
Mac       : 00:09:B0:XX:XX:XX                                                                                                      
Room      : 3                                                                                                                                                            
Parent    : 0 

Device Volume : 37

My next step is now to move Device id directly into definition like {device.name,67} so we can use different device in one line

New version 0.2 just published.

See previous post for details, and roadmap.

A few tweaks:

a) eliminate use of [tt]loadstring(…)[/tt], to avoid potential “abuse” of the overall syntax.
Effectively [tt]loadstring[/tt] is akin to the things that are used to cause XSS and SQL injection attacks so I’ve changed these to a more explicit [tt]luup.*[/tt] request to avoid the injection possibilities.

This code is not yet tested, but it should function correctly with a little tweaking. Note that the [tt]pcall[/tt] hooks need to be added back in.

b) Dual-code to handle “testing” the output completely outside of Vera
I have [crude] wrapper method implementations that let me run the codebase outside of Vera, for functional/syntax testing, as well as inside Vera for functional/integration testing. It’s important to keep this code fairly Luup-neutral so that tests can be added without having to embed it into Vera. There are now separate stub-functions for both Luup and pure implementations.

c) Addition of array-like syntax for Devices.
eg. you can now reference devices as [tt]device[82].name[/tt], as well as [tt]device.name[/tt]. The latter requires the deviceId parameter to be passed, and will most likely be the common [re-usable] snippet syntax in Scenes (etc). The former allows for referencing arbitrary devices.
Future versions of this syntax can be expanded to permit a named-device syntax like [tt]device[“Kitchen Light”].name[/tt] if we’re interested (*)… with associated problems…

This should only be considered a working prototype, one that helps us understand if this is the syntax we really want, or if we want another. We still need to really have that discussion, which was the intent of this thread. I’ve worked this prototype as a means to ensure that any proposal also has a vetted/trial implementation so people can see what it really looks like.

The following examples are now understood by this code. They all take the general form:
{value}
{value,dataType}
{value,dataType,formatString}

where

[ul][li]value - a string of the form [tt]system.[/tt], [tt]device.[/tt], [tt]device[deviceId].*[/tt][/li]
[li]dataType - a datatype of either [tt]String[/tt], [tt]Number[/tt] or [tt]Date[/tt]. The default, if not specified, is [tt]String[/tt][/li]
[li]formatString - a datatype-specific format identifier used for layout/formatting of the output[/li][/ul]

Values... {system.latitude} {system.la4-titude} {system.timezone} {system.city} {device.name} {device[82].name} {device.ip} {device[82].ip} {device.mac} {device[82].mac} {device.parent.name} {device[82].parent.name} {device.service[urn:micasaverde-com:serviceId:SecuritySensor1].LastTrip} {device[82].service[urn:micasaverde-com:serviceId:SecuritySensor1].LastTrip} {device.service[].LastTrip} {device[82].service[].LastTrip} {device.room.name} {device[82].room.name} {device.parent.room.name} {device[82].parent.room.name} Formatting... Number values we dont understand {5.5}, {5.5,Number}, {5.5,Number,%10.4f} Number values we understand {pi}, {pi,Number}, {pi,Number,%10.3f} String values we dont understand {title} Dates we understand {currentTime}, {currentTime,Date}, {currentTime,Date,%x}, {currentTime,Date,%H:%M}, {currentTime,Date,"%d.%m.%Y"}

Luup is not defined Values... luup.latitude INVALID luup.timezone luup.city luup.attr_get("name",57) luup.attr_get("name",82) luup.attr_get("ip",57) luup.attr_get("ip",82) luup.attr_get("mac",57) luup.attr_get("mac",82) luup.attr_get("name",luup.devices[57].parent_num) luup.attr_get("name",luup.devices[82].parent_num) luup.variable_get("urn:micasaverde-com:serviceId:SecuritySensor1","LastTrip",57) luup.variable_get("urn:micasaverde-com:serviceId:SecuritySensor1","LastTrip",82) INVALID INVALID luup.rooms[luup.devices[57].room_num] luup.rooms[luup.devices[82].room_num] luup.rooms[luup.devices[luup.devices[57].parent_num].room_num] luup.rooms[luup.devices[luup.devices[82].parent_num].room_num] Formatting... Number values we dont understand {5.5}, {5.5,Number}, {5.5,Number,%10.4f} Number values we understand 3.141592654, 3.141592654, 3.142 String values we dont understand {title} Dates we understand 1346041025, Sun Aug 26 21:17:05 2012, 08/26/12, 21:17, "26.08.2012"

For me, the “dual syntax” is a source of confusion. I think it will be easier to always use syntax like device[82].name it’ll be easier to use and to understand, and easier to read (No need to go to the end of text to know device_id )

For me, we also need to add some Formating Tag like {CR}{LF}{TAB} … to allow some basic text formating when we need to pass parameters via an input field and need a result with multiple lines.

The use of [tt]device.*[/tt], without the array offset, was intended to be used in cases where there was a clear “device” associated with the message text.

In general, I’d not expect consumers to directly call the [tt]resolve[/tt] method of this library, but more that they’d be specifying strings against a plugin that, in turn, would call something like it internally.

For an Alarm Panel, for example, I’d expect users to be able to [generically] write a message string like the following, in the overall configuration of the Alarm Plugin:
[tt] The Window {device.name} is open at time {system.currentTime,Date}[/tt]

At runtime, the Alarm plugin would then do something with that value, resolving the “this” reference to the device’s Id at runtime. This keeps things simple for a common case, since lots of people have trouble with deviceId’s (vs nodeId’s).

Perhaps a clearer model would be to use [tt]devices[/tt] (plural), for the array-access case, and that the following could be equivalent:
[tt] device = devices[this][/tt]

where “this” is defined by the Plugin that’s invoking the library, so that the user isn’t required to juggle deviceId’s (for common cases)

For me, we also need to add some Formating Tag like {CR}{LF}{TAB} .. to allow some basic text formating when we need to pass parameters via an input field and need a result with multiple lines.
The standard ones are already defined, for tab/cr/lf (etc) http://www.lua.org/manual/5.1/manual.html#2.1

They should suffice for most. They won’t work in the block-style string declaration I use in the example, but they do work for regular strings.

Sorry guys, I haven’t said much since starting this thread. I like the way it’s going. Keep it up!

One interesting possibility is for device-specific extensions to the scheme.

For example, we could have a syntax like:
[tt]device.variable.some.custom.string[/tt]
[tt]devices[56].variable.some.custom.string[/tt]

In this case, when the token “[tt]variable[/tt]” is received, we simply call a pre-defined service, on the resolved Device, and pass it the remainder of the string (“[tt]some.custom.string[/tt]”, in this case) to let it resolve how to fetch the requested value.

This would allow for interesting examples like:

[tt] device.variable.LastTrip
devices[56].variable.LastTrip[/tt]

where the device itself is actively involved in working out how to handle “[tt]LastTrip[/tt]”.

or ones like:
[tt] device.variable.tomorrow.Temperature
devices[56].variable.tomorrow.Temperature
devices[“Weather”].variable.tomorrow.Temperature[/tt]

The challenge here is that there’s no clear way to get a value back from a UPnP Service from a Vera device (you can call actions, they just can’t return anything) so it would require some tricks with Lua libs or something similar that each device would have to provide.

Technically, we could leave out the keyword (“variable”, in this case) and simply do a pass-through of everything we don’t understand, but that might limit our options in the future.

What’s needed to drive the core of this to agreement (format, etc)?

There are cases coming up, like the TTS stuff, that could benefit from the use of a library that supports Text Templating/Expression language to let users “embed” values from Vera within their output.

I can think of a couple of additions:

  1. Register some template expansion handlers … to expand the syntax translation capability … return true if expanded … otherwise walk the list of handlers.
  2. Need to provide a dictionary of variables into the expansion library … So they know what “device” is … maybe even currenttime since it might be possible for it to change between multiple templates … also if I have an expansion handler … I can add the definition of say “relay” if I have relay objects to format.
  3. A LastChance handler can have one of the following behaviors:
    a) Leave unexpanded templates alone. {foobar} → {foobar}
    b) Strip the template {foobar} →
    c} Strip the “{” and “}” {foobar} → foobar
  4. You should plan on nested templates:
    {currentTime,Date,{device.dateformat}}
  5. Can a Template expand into something that has an embeded template that also needs to be expanded (i.e. late vs early identification of templates) ?

I assume that device.service[].LastTrip will search all service variables for “LastTrip”
Why not just use: device.LastTrip to return attributes first … then search for service variables. Since this will likely end up in end user format strings … most of which do not know the difference anyway.

It should be obvious how/who should expand a template.
So I have problems with your time format.
It should be {DateTime, currentime}
I might want to format the LastTrip time {DateTime, {device.LastTrip}, {device.dateformat}}

Similarly numbers should ALWAYS start with {Number … i.e. {Number, Number.pi} {Number, Number.pi, %10.4f} {Number, 5.5} … actually I think the example should be {Number, {Number.pi}, %10.4f}
Would the following be supported {Number, 5.5, %{device.FieldWidth}.{device.Precision}}

Probably need some string Manipulation expansions … trim … fill … to-upper … to-lower …

I have written some lua code that does what guessed and I want …
Functionality from guessed … some modified syntax
Extensibility
Templates can have embeded templates
Templates can expand to other templates that need to be expanded
Template for String class … that allows string formating (Length, left and right fill) does a little more than string.format

But I want to be able to access Service variables without knowing the service names …
I would like to access all of the service variables that are listed on the “Advanced” tab for a device. (i.e. the services that been used to create a variable for a particular device)

Anybody have a clue how to get that in lua ?

I can get it from Vera’s system json file.

In Lua, you can’t as the API is incomplete. This is why I don’t do things like this:

I assume that device.service[].LastTrip will search all service variables for "LastTrip"
You could do something "approximate" for the common case, but you'd need a map of the serviceId's to use for common variable names. It may still be worthwhile doing that, since it would provide a predictable value/response in all cases.i ie. avoiding breakage when new services are added to existing objects (akin to the problems with [tt]import foo.*[/tt] in Java)

The UI can do it because it’s reading the JSON, but that’s not practical to do it Lua (It’s huge!).

It should be {DateTime, currentime} I might want to format the LastTrip time {DateTime, {device.LastTrip}, {device.dateformat}}
I'm not sure I understand the reasoning here. Can you explain further?

My goal was to make it easy for users, so having to declare “upfront” what the type was, if all they wanted was “default formatting”, didn’t seem to fit that goal.

Actually, the only reason to do it at all is because all inputs are string (from Luup, at least) and there are separate formatters (sprintf style for string, number and os.date for dates). Without the type description, you’d have to “guess” and/or apply each and see which one fails.

Similarly numbers should ALWAYS start with {Number ... i.e. {Number, Number.pi} {Number, Number.pi, %10.4f} {Number, 5.5} .... actually I think the example should be {Number, {Number.pi}, %10.4f} Would the following be supported {Number, 5.5, %{device.FieldWidth}.{device.Precision}}
Again, the goal was to make something friendly for users... esp in the case where they just want default formatting. Having to prepend "Number" breaks that somewhat (IMHO) for the simple case.
Probably need some string Manipulation expansions ... trim ... fill ... to-upper ... to-lower ...
We'll need to be able to do it down the road, but I'd leave it out "upfront" so that feedback can be gotten on the core... If changes result, then we don't break all the other items that get shoehorned into a first-release.
1) Register some template expansion handlers ... to expand the syntax translation capability ... return true if expanded ... otherwise walk the list of handlers.
I was intentionally leaving that out of the prototype so we could focus on core syntax issues. We'd need to be able to add it later, so the syntax and structure would need to be able to support it at a later date, but I wanted to keep it simple for the first round (to avoid building yet-another complex language for people to learn).

For similar reasons, I didn’t put conditionals (etc) into it, even though they’d be handy for the Sonos DIDL-Lite printing that I hard-coded recently.

aka. “[tt][/tt]” in JSTL or [tt]{x,choice, …}[/tt] in MessageBundle.

I guess I can have a lookup table of know DeviceType → ServiceName → Variable Name to use when the service is not know.
Here is the test program for my template expansion.
Note It’s easy to tell which extendable “Class” is responsible for expanding the template.
The Template prefix is common … after the {Class you help class specific syntax to math the desired semantics.

function test()
   t = require("veraTemplate")
   t.Absent = 2
   d = require("veraTemplateDevice")
   -- Set the Default Device
   d.device = 123

   print(t.expand("Test Number {Number, 3.14159}  !"))
   print(t.expand("Test Number {Number.format,%20.2e,3.14159}  !"))
   print(t.expand("Test DateTime {dAtEtIme,currenttime}  !"))
   print(t.expand("Test DateTime {DateTime.format,%A %B %d %Y %H:%M,currenttime}  !"))
   print(t.expand("Test DateTime {DateTime.format,%A %B %d %Y %H:%M,1111100000}  !"))
   print(t.expand("Test String {String.format,%20s %-20s,String 1,String 2}  !"))
   print(t.expand("Test String {String.rfill,{String.lfill,{String.format,%20s %-20s,String 1,String 2},L},R}  !"))
   print(t.expand("Test String {String.format(Line1\nLine2\nLine3}  !"))
   print(t.expand("Test System {System.city}  !"))

   print(t.expand("Test Device {device}  !"))
   print(t.expand("Test Device {device.description}  !"))
   print(t.expand("Test Device {device.room}  !"))
   print(t.expand("Test Device {device.room.name}  !"))
   print(t.expand("Test Device {device.service[].name}  !"))
   print(t.expand("Test Device {device.service[baz].name}  !"))

   print(t.expand("Test Device {device[5]}  !"))
   print(t.expand("Test Device {device[5].description}  !"))
   print(t.expand("Test Device {device[5].room}  !"))
   print(t.expand("Test Device {device[5].room.name}  !"))
   print(t.expand("Test Device {device[5].service[].name}  !"))
   print(t.expand("Test Device {device[5].service[baz].name}  !"))

   print(t.expand("Test Device {device[foo]}  !"))
   print(t.expand("Test Device {device[foo].description}  !"))
   print(t.expand("Test Device {device[foo].room}  !"))
   print(t.expand("Test Device {device[foo].room.name}  !"))
   print(t.expand("Test Device {device[foo].service[].name}  !"))
   print(t.expand("Test Device {device[foo].service[baz].name}  !"))

   print(t.expand("Test Device {device[foo].parent}  !"))
   print(t.expand("Test Device {device[foo].parent.description}  !"))
   print(t.expand("Test Device {device[foo].parent.room}  !"))
   print(t.expand("Test Device {device[foo].parent.room.name}  !"))
   print(t.expand("Test Device {device[foo].parent.service[].name}  !"))
   print(t.expand("Test Device {device[foo].parent.service[baz].name}  !"))

   print("Parse Classes:")
   table.foreach(t.classes(), print)
end

With the following output (Absent set to Remove)
Also I test for luup to use luup code or show the luup code that would be used for the value.

Test Number            3.14e+000  !
Test DateTime 1348442839  !
Test DateTime Sunday September 23 2012 18:27  !
Test DateTime Thursday March 17 2005 17:53  !
Test String             String 1 String 2              !
Test String LLLLLLLLLLLLString 1 String 2RRRRRRRRRRRR  !
Test String .format(Line1
Line2
Line3  !
Test System luup.city  !
Test Device 123  !
Test Device luup.devices[123].description  !
Test Device luup.devices[123].room_num  !
Test Device luup.rooms[luup.devices[123].room_num]  !
Test Device   !
Test Device luup.variable_get(baz, name, 123)  !
Test Device 5  !
Test Device luup.devices[5].description  !
Test Device luup.devices[5].room_num  !
Test Device luup.rooms[luup.devices[5].room_num]  !
Test Device   !
Test Device luup.variable_get(baz, name, 5)  !
Test Device foo  !
Test Device luup.devices[foo].description  !
Test Device luup.devices[foo].room_num  !
Test Device luup.rooms[luup.devices[foo].room_num]  !
Test Device   !
Test Device luup.variable_get(baz, name, foo)  !
Test Device luup.devices[foo].parent  !
Test Device luup.devices[luup.devices[foo].parent].description  !
Test Device luup.devices[luup.devices[foo].parent].room_num  !
Test Device luup.rooms[luup.devices[luup.devices[foo].parent].room_num]  !
Test Device   !
Test Device luup.variable_get(baz, name, luup.devices[foo].parent)  !
Parse Classes:
1	datetime
2	string
3	device
4	number
5	system

I think it would be easy to add some conditional templates and/or some iteration templates.
Once again … the issue would be the desired semantics and corresponding syntax.

I still think it’s confusing to have the thing that you want to print/display being in different locations, depending upon the other parameters.

In your proposal, you have it sometimes in the first slot & sometimes in the last slot.

I’m not convinced that’s easy for users to understand. For the most part, they just want to “print” the value of something, and optionally format it. If we put the parameters into an order that matches [a guess at] user expectation then I think it’ll be easier for them to uptake.

In the majority case, I don’t think they’ll format their results (except for the EPOCH stuff), but when they do the question is where should they “augment” the statement to get it… and what’s the most logical to non-programmers.

Thoughts?

BTW: I meant to change the last code so that it used [tt]devices[index].stuff[/tt] (plural) for the list-of, and [tt]device.stuff[/tt] (singular) for the default-device construct, to avoid confusion. This was based upon feedback from @Massalia above.

I understand your comments about usability … but if that’s the primary driver than you can very easily limit extensibility … or you will have chaos in variety of template formats … So in that sense standardization trumps for me … If you prefer devices over device with the [] syntax than you are letting your computer background influence you … because from a user perspective you are dealing with only the identified device!

Actually I think number semantics should go … String.format can already format any number (or an existing string representation of a number) in the desired manner and in my implementation I just defer to it.

I think the most popular syntax would be:
{device[5].service[].name}
Actually I would like for:
{device[5].name} to try Device properties first, than service variables … this makes it very simple. It’s the view a user has on the Advanced tab in the UI.

Then I think {string.format, FormatString , template 1, template 2, template 3, …} would be next.

For TTS you WILL most likely want to format the string … often you will need to add punctuation characters to get the speaking cadence right. That’s one of my motivations.

My second motivation is a standardized notification system for Vera … everywhere you currently have a checkbox on the UI for notification I would like to have a place to put an alternate template string … and of course I would like my favorite notification plugin (VeraAlert is my plugin of choice) to deliver this notification. I just added TTS to it as one of it’s alert types. A template in the notification message is how identify how the notification is delivered … i.e. {alert:tts} or {alert:1} … the latter is alert tone #1.

We can do better standard notification templates if we had more context for the places a notification or Luup script can be run. This would allow us to format how we want notifications based on where the notification is going. But as a SW guy I can live without it … that’s what luup code is for … it just means redundancy and associated maintenance in the Vera UI setup and the related Luup code!

I wish we had more context for our Luup code/Template Strings in the various contexts:

[ul][li]Startup Luup - Why we restarted[/li]
[li]Scene Event Trigger Luup - Trigger Device, Trigger Value, Associated Scene[/li]
[li]Scene Luup - Scene, Trigger, Schedule, Associated Device Actions[/li]
[li]Notifications - Device, Notification Variable[/li][/ul]

Ultimately, I’m doing this for the entry-level users that need “just a little more” than the standard Scene parameter specification provides them today.

The use cases that [right now] are most common are:
a) Sending an Email in a Scene, based upon different events - controlling the format
This one has come up a lot over the last 3yrs. Given that MCV has done nothing in this space, and we’re sprouting notification plugins, we need a simple way for users to specify both the subject and body (for email) with total control over the formatting - to tailor it to their tastes without Lua.

The likely things they’ll include are:
a1) a fixed wrapper message
a2) the date of the event as a readable string, optionally in a format of their choice
a3) the date of a LastUpdate from some device (this is in EPOCH right now, will require conversion to their format)
a4) Ability to translate 0/1 values into “Open/Closed” (or “On/Off” for lighting, Tripped etc) for some of the service variables, or the language-specific versions of those.

b) TTS Conversions/Output

c) UPnP
There are a bunch of cases where UPnP calls have output that needs to be “fed into” the next step in a Scene. Right now there is no way to do this, but if all Scene inputs had a way to read values directly from state vars, then we’d have a way for the simple cases.

This happens, for example, when you need to use TTS. In this case, you need to save a Playlist, and get an ID, then you “Say” something, then you restore the playlist from the ID in step 1.
Right now, this forces you into Lua code.

All of these items need to be do-able without Lua, and without needing a programming degree 8)
Personally I’ll use Lua for all the stuff I’m doing, so I don’t need complex EL infrastructure. What I’m looking to do is lessen the # of cases where people have to jump into Lua for the simple stuff. If, as an added bonus, we can extend to the complex then that’s great, but my first goal is end-user simplicity.

If you prefer devices over device with the [] syntax than you are letting your computer background influence you ... because from a user perspective you are dealing with only the identified device!
Actually this wasn't for me, it was for another commenter. Personally I'd leave it at "device", with the dual-use, but there was commentary that I was looking to handle and get feedback on so I used the dual-mode device /devices to drive that feedback.
Actually I would like for: {device[5].name} to try Device properties first, than service variables .. this makes it very simple. It's the view a user has on the Advanced tab in the UI.
Right, for the attr stuff, that should definitely be top-level. Things like "ip", "name" etc are attributes so they're already treated differently internally.

I would say that some service variables, ones that are really common, should also have aliases at the top level also. Things like “LastUpdate”, “Tripped”, “Status”, “LoadLevelStatus”, “BatteryLevel” and “Watts” perhaps, since they’re likely to be used in alerts. If we want this, then we should build a list to agree upon.

My second motivation is a standardized notification system for Vera .. everywhere you currently have a checkbox on the UI for notification I would like to have a place to put an alternate template string ... and of course I would like my favorite notification plugin (VeraAlert is my plugin of choice) to deliver this notification.
We have, for a long time now, asked MCV to make the notifications mechanism plugable... Let the user have a choice of registering one or more plugins, with the current scheme just being one, to perform notifications.

Sadly, this falls on deaf ears… except for some crazy-back-arsed scheme that was added a few months back… and probably will never get used by avg-joe.

If implemented, this would provide yet-another mechanism for people to extract themselves from the mios.com infrastructure, and to get more reliable messaging overall (at a cost-savings to MCV also)