Expressions and LuaXP Functions
-
I suggest adding both comments (
#
to end of line is a comment in lexpjs) and indent spaces for clarity (e.g. thematch
clause on the second line is part of theeach
on the first line, so IMO an indent would make this much more clear).That search on the first two lines suggests that MSR may need a deterministic way to differentiate an entity that represents a device from entities that represent other system data. I'll think about that, because it has come up before.
Another way to write the test on line 4 is
'dimming' in getEntity( dev ).capabilities
. Thecapabilities
property is an object, so thein
operator works by directly testing on that object, and will be much faster than usingindexof()
on the actions array. Given, this operation probably would not run frequently, so that kind of optimization may be overkill, but I assert that this alternative to the array search is actually easier to interpret as well, and where performance is not the leading goal, clarity is a good next candidate.The inline assignment to a temporary variable on 5 and used in line 6 is clever and makes me cackle with glee.
-
@toggledbits and other puzzle lovers... I challenge you to paste in this Expression definition and run it with the 'try' button, to solve this heavily obfuscated conundrum:
solve="<creator_mystery>"; s1="(.)";s2="(..)";s3="(...)";s4=s1+s1;s5=s2+s2;s6=s3+s3;who=getEntity(replace(solve,s4+s6+s4+s4+s5+s1,"\$3\$2\$4\$5\$8\$7\$8\$9\$6\$11\$8\$7\$8\$9\$6")); you=who[keys(who)[0x07]];are=you[keys(you)[0b01]];upper(substr(are[keys(are)[0o10]],0,0o13))
|-Answer: _____________°fa-info°(S┴IqpƎ˥פפO┴)-|
-
@toggledbits , how does MSR "know" that an expression like:
each dev in devs: getEntity(dev).attributes.power_switch.state ? dev : null
contains dependencies on the status of (potentially) many devices?
It's neat that MSR almost immediately detects when a light gets turned on, for instance, and the resulting array grows by one within less than a second. But what is cuing MSR to re-evaluate the above expression so quickly behind the scenes?
-
If
devs
is a constant list, the iteration visits eachdev
throughgetEntity()
, and that function subscribes to any device it is asked to resolve. So the first time the expression is evaluated, it has subscribed to every device indevs
. -
@toggledbits meanwhile, as I watch the steady stream of "changed entities" (involving devices that are otherwise just minding their own business, doing nothing) going by on
Status
, such as:Sound Bar Beam vera>device_322 Playing 17:39:11 Sofa Lamp vera>device_138 false 17:39:00
do you recommend any changes to Vera in order to throttle that traffic, or is it just customary Z-Wave polling in action?
-
The rule or expression isn't reacting to every device, only those devices that end up being a target of
getEntity()
. The polling activity will cause additional evaluations, but it causes little actual additional load on the (MSR) system, certainly not enough to make it worth your time to try to put a dent in it. Party on! -
SORTING AN ARRAY IN MSR EXPRESSIONS
I somehow doubt this will find much utility among most users' workflows, but here goes...
If you want to sort a particular 1-dimensional numerical array:a=[8,3,11,0,5], #unsorted array L=len(a)-1, each i in 0..L: #repeat for each value in a do b=a[0], #beginning with first value in array each j in 0..(L-i): #compare remaining values if min(b,num=a[j])<b then b=num else b endif, remove(a,indexOf(a,b)), #remove b from array b #was lowest number found in original array done
// result (array)
[ 0 , 3 , 5 , 8 , 11 ]
PRO TIP: To sort an array from another expression, simply edit the first line to something like this:
a = clone(otherExp),
UPDATE: As of release 21153, in which
min()
may take an array as its argument, the following expression works more cleanly:a=[8,3,11,0,5], each n in a: do b=min(a), remove(a,indexOf(a,b)), b done
-
CREATING AN ARRAY OF STEPPED VALUES
In case you're looking for a numerical array containing evenly-spaced (linear)
DIM
values in the range0
to1
and back to0
, this expression should fill the bill:each s in split(join(0..10)+","+join(9..0),","): int(s)/10
// result (array) [0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1,0.9,0.8,0.7,0.6,0.5,0.4,0.3,0.2,0.1,0]
-
Also works:
each n in -10..10: (10-abs(n))/10
. -
ARRAY OF RAMPED
DIM
VALUES USING SINE WAVE PATTERNpi=3.1415, steps=10; each step in 0..steps: round(sin(step*(pi/(2*steps))),2)
// result (array) [0,0.16,0.31,0.45,0.59,0.71,0.81,0.89,0.95,0.99,1]
NOTE: You can change the number of
steps
to any integer value. -
ROTATE SET OF LIGHTS
Sometimes you might want a set of lights to swap "On"/"Off" states -- not "toggle" per se, just follow each other based on their starting status, which I'm calling "rotation." (I believe the technical term is "chasing.")
lites=["vera>device_8","vera>device_9","vera>device_110"], patt=each L in lites: getEntity(L).attributes.power_switch.state, new=slice(push(patt,patt[0]),1), each v,k in new: performAction(lites[k],"power_switch."+(v?"on":"off"),{})
USAGE
Edit the list inlites
based on which devices you want to participate in the rotation. Every time this expression evaluates, the on/off states will rotate by one: i.e.010
►100
►001
, etc. -
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.@toggledbits any chance we'll see strftime function coming up "soon"? Or advice how to implement the same with lexpjs (when you have unix timestamp, RfV example below)?
strftime("%H:%M %d/%m/%y", <timestamp> )
-
T toggledbits locked this topic on