Expressions and LuaXP Functions
-
lexpjs is missing some functions that LuaXP had and range from convenience to vital. Seems that date/time formatting is among them, as this should be a simpler operation, and we can take advantage of all the localization that the system itself offers in the process.
Among the functions I know are missing, or I'm pondering, are:
format( template, ...args )
- A C-style formatter for strings (likesprintf()
)
strftime( template, time )
- Also C-style, formatting specifically for date parts.
dateadd( time, yy, mm, dd, hh, MM, ss )
- Modify a timestamp; e.g.dateadd( time(), null, null, 1 )
would add one day to the current time (so the result time would have the same hour, minute, second just a day forward)
dateset( time, yy, mm, dd, hh, MM, ss )
- Another way to modify a timestamp, e.g.dateset( time(), null, null, null, 0, 0, 0 )
would result in midnight for the current date, whiledateset( time(), null, null, null, 24, 0, 0 )
would be exactly midnight tomorrow.
push
,pop
,shift
andunshift
- Array functions to move an element in or out.
concat
- Concatenate two arrays; function, or overload+
( e.g.[1,2,3] + [4,5,6] => [1,2,3,4,5,6]
)
slice
- Return a portion of arrray; pondering whether this should be done as a function, or with python-stylearray[start:end]
syntax (into the whole brevity thing);
splice
- Remove elements from, and insert into, and array;splice()
is the JavaScript function, and it's very powerful but not particularly user-friendly. Maybe better to have separateremove()
andinsert()
?The HubitatController also extends lexpjs with its own private
hsltorgb()
andrgbtohsl()
functions to convert between RGB and HS(L) as part of its entity mappings. I'm considering making these available everywhere (could be useful in some actions in particular on any platform).You may note that map/reduce functions are missing, but they are covered by the
each
operator (e.g.each val in numbers: val**2
produces a new array of the square of the values innumbers
, themap()
function, whilet=0, each val in squares: t=t+val
is a reduce that produces the sum of the squares); theselect()
function from LuaXP is covered by thefirst
option (e.g.first element in array-or-object with match-expression
). And filtering can also be done witheach
because any null result during iteration isn't added to the result array. -
Wonderful!
COMMENTS:
- Yes, brevity counts.
- Did not know LuaXP (much less lexpjs) has exponentiation with
**
, only knewpow( )
- Hadn't used some of the older syntax much like
pop
orconcat
, but other power users sure did - YES to HSL◄►RGB conversion
- I'm new to
each
,first
anddo
but gosh are they powerful!
Guess you'll have your hands full deciding which to implement and then fleshing out the lexpjs docs!
Looking forward to it. Thanks again. -
No equivalent function, but the name is available as
getEntity( .... ).name
-
I'll try that, and encourage you to add that to drop-down.
Also, meanwhile, I'm getting a (null) response with:
getEntity( "vera>device_337" ).attributes.x_vera_device.device_number
which IMHO ought to be(number) 337
, no?
EDIT: Solved with a hard refresh of browser! Weird. -
I'm going to enhance the following functions to work like this. I use the "old school" *nix notation for optional parameters by enclosing in square brackets, so where you see that below, those parameters are optional.
match( string, pattern [ , num [ , flags ] ] )
- Find pattern in string. If num is given and the pattern contains regexp groups, the function will return the numth matched group (e.g.match( "The rain in Spain", "r(ai)n", 1 )
returns "ai"; zero is always the full match, and thus the default). If flags is given as "i", a case-insensitive match will be performed.find( string, pattern [ , flags ] )
- Returns the position (0-based) of the matched pattern in string. Returns -1 if the pattern is not matched. The flags have the same meaning as inmatch()
.replace( string, pattern, replacement [ , flags ] )
- Replaces the first occurrence (by default) of pattern in string with replacement. If specified, flags have the same meaning as inmatch()
, plus the additional flag "g" (for global) modifies the behavior to replace all instances of the pattern in string (if needed, multiple flags can simply be concatenated, as in "ig" for case-insensitive + global). The replacement string uses$
as a special pattern lead-in, so $` yields the portion of string before the matched pattern,$'
yields the portion of string after the matched pattern,$&
is the entire matched substring,$n
inserts the match of the nth group in pattern, and$$
yields "$" (self-escape).
Basically, all of this follows JavaScript semantics, which provides the underlying implementation.
-
In what context?
-
Yes, will happen. It raises priority on another "to-do" item for the parser, which has rather "blunt" string lexing at the moment. I've got a more formal version working now, but I want to play with it a little more before I commit it to MSR.
-
@toggledbits I can't seem to concoct a meaningful example of
DO
..DONE
, since I'm probably not grasping its intended function within the MSR framework.
Does it have a use here, and if so, how would such a statement look? Thanks! -
do...done
is intended to help thefirst
andeach
statements (but can be used anywhere, really).- the
each
andfirst
statements operate only on a single expression; do...done
encapsulates multiple expressions (separated by comma), but looks like a single expression toeach
andfirst
;- The result of a
do...done
statement is the value of the last expression executed within it.
Here from my own configuration is an example:
each detector in leak_detectors: do d=getEntity(detector), d.attributes.arming.state && d.attributes.binary_sensor.state ? d.id : null done
There's a separate expression called
leak_detectors
that provides an array of leak detector entity IDs (canonical, across controllers, of course). Theeach
statement iterates over each of them. Thedo...done
encapsulates two expressions: the fetch of the entity stored to a local variable in the first, and then uses that local variable twice in the second expression. The result of thedo...done
is the result of the last expression, which is the detector's ID if it is armed and tripped, or null otherwise. With aneach
expression, anynull
s do not appear in the final array.The idea should be familiar from RFV, although the construction is a little different: it's a loop over a list of known leak detectors and builds an array of those that are armed and in alarm (if any, hopefully not).
- the
-
For a lark, I defined a test variable as:
testObj = { a:5, b:2, c:"cat"}, each num in values(testObj): r = isNaN(num) ? " That's all!" : "Libra owns " + num + " cats"
which spits out:
["Libra owns 5 cats","Libra owns 2 cats","Libra owns cat cats"]
while I had actually expected (due to the
NaN
:["Libra owns 5 cats","Libra owns 2 cats"," That's all!"]
Also, am noticing that the Expression 'value' windows always contract to their initial (1.25-line tall) height, rather than remember what the user enlarged them to.
Are both of these "expected behaviors" in MSR??
-
The
isNaN()
is a yes... unlike JavaScript, the lexpjsisNaN()
literally tests to see if the argument isNaN
, which"cat"
is not. You'd need to write your test asr = isNaN( int( num ) ) ? ...etc.
I can't store your selected/custom field sizes. I haven't poked harder into getting it to auto-size. I've done that before, but for some reason it isn't working and I haven't bothered to track it down yet.
-
Yes, assignment to simple variables only; the left side of an assignment can only be an identifier (right now).
-
This one's got me stumped now:
testCalc
=each num in [4,5,6]: do [num, num+1, num+2] done
yields:
[[4,5,6],[4,5,6],[4,5,6]]
instead of:
[[4,5,6],[5,6,7],[6,7,8]]
Or have I stumbled again? (I do realize the DO..DONE is a red herring, but removing it doesn't change the result.)
-
Nope, that's clearly wrong. Not hard to fix. I found a spot where the executive was rewriting a variable rather than returning a copy, specific to arrays, so this particular test would hit it, any other data type would not. Good catch!