Expressions and LuaXP Functions
-
toggledbitswrote on May 2, 2021, 6:50 PM last edited by toggledbits May 2, 2021, 2:54 PM
You can't compare arrays (or objects) with ==. What it's telling you when you write
a==b
is that neither a nor b are null, 0, or false, and they are identical references. So yes, this is all expected.As for it comporting with user expectations, if the user is a programmer, I think this will be no surprise. Non-programmers have the learning curve. Again, another reason I really don't like guiding users to expressions. New users, IMO, should be blissfully unaware of their existence.
-
Wise words!
-
toggledbitswrote on May 2, 2021, 10:13 PM last edited by toggledbits May 3, 2021, 7:23 AM
By the way, I forgot to mention, as is often necessary in many languages because of the reference issue, there is a
clone()
function that makes a new copy of an existing array or object. Soa=[1,2,3],b=clone(a),push(b,99)
will result ina
having[1,2,3]
andb
having[1,2,3,99]
. -
It's as if you read my mind. I suppose my numerous "cloning arrays" examples gave it away.
Without
clone()
, I was never (easily) able to manipulate a copied array without affecting the original, and that was driving me bonkers.Thanks!
-
@toggledbits , am digging how the new
a..b
operator behaves predictably, fixing the decimal ofa
and spitting out integer steps up to≤b
when botha
,b
> 0 anda
<b
. For example:a = 1 .. 3.3 // result (array) [1,2,3] a = 1.1 .. 2.3 // result (array) (array) [1.1,2.1]
But sometimes it tries too hard, particularly in this case where
a
< 0:a = -2.3 .. 1.1 // result (array) [-2.3,-1.2999999999999998,-0.2999999999999998,0.7000000000000002]
or here, where
a
>b
:a = 2.3 .. 1.1 // result (array) [2.3,1.2999999999999998]
All testing thus far using only INTEGERS has avoided any rounding issues, so that will likely become my mantra on usage.
rev. 21123 -
LibraSunwrote on May 14, 2021, 2:42 PM last edited by LibraSun May 14, 2021, 2:30 PM
KEEP TURNING OFF LIGHTS!
I thought it might be fun to have a Rule that periodically watches for lights (any device, really) left on, but I wanted the entire "action" part of the Rule to live inside a single expression. Here's what I came up with:
devs = each dev in getEntity( "vera>controller_all" ).members: match( dev , ".*device_\\d+" ), devices = each dev in devs: indexOf( getEntity( dev ).actions, "dimming.set" ) > -1 ? dev : null, performAction( f = first dev in devices with getEntity( dev ).attributes.power_switch.state, "power_switch.off", { } ), getEntity( f ).name + " turned off."
This example only checks dimmable lights paired with Vera, but could easily be modified to include other classes of device connected to other controllers, etc.
The way it works is it (Line 1) pulls all of the member devices associated with Vera; (Line 2) filters the list to disregard devices not of the formdevice_NNN
; (Lines 3-4) checks to see whether each device handles dimming; and, (Line 5) passes along only those forperformAction()
to turn off individually (one "On" device per cycle); finally, (Line 6) the name of the most recently turned-off device is logged.I concocted this behemoth because my Vera routinely chokes on my "Lights Out" rule, which attempts to switch 20 devices OFF all at once. I felt it might be more prudent to handle the switching at a slower pace, with the added benefit that if anyone turns ON one of the listed devices during "Lights Out" time, this rule would spot it and turn it back off automatically.
Comments?
- Libra
-
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.
-
LibraSunwrote on May 14, 2021, 10:50 PM last edited by LibraSun May 15, 2021, 1:04 PM
@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
. -
LibraSunwrote on May 25, 2021, 10:41 PM last edited by LibraSun May 25, 2021, 6:41 PM
@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
-
LibraSunwrote on May 28, 2021, 1:25 AM last edited by LibraSun May 27, 2021, 9:26 PM
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
. -
LibraSunwrote on May 28, 2021, 1:38 AM last edited by LibraSun May 27, 2021, 9:39 PM
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. -
LibraSunwrote on May 29, 2021, 2:47 AM last edited by LibraSun May 28, 2021, 10:56 PM
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. -
tunnusreplied to toggledbits on Aug 13, 2021, 9:56 AM last edited by tunnus Aug 13, 2021, 6:38 AM
@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> )
-