Expressions and LuaXP Functions
-
toggledbitswrote on Mar 15, 2021, 3:21 AM last edited by toggledbits Mar 14, 2021, 11:23 PM
I may have to re-route how that function works. Right now, it sends the request via the websocket to the WSAPI, which asks the entity to perform the action on itself. It will take a bit of plumbing, but I can probably redirect that process to the Engine, where the sub can take place. The trick is rule-based, because they set up context very differently from global, and the rule sets up context for the engine (so if I'm asking the engine to do something for the rule, the context hasn't been established... yeah, going to take some noodling...).
-
◄— this guy effed up
I was entirely mistaken about MSR not substituting variables correctly in my workflow. The problem, alas, was entirely of my own making (e.g. not grasping that running internal portions of a Rule manually would NOT invoke substitution(s) the way I had surmised). Secondly, I had crafted entirely too complex a workflow for a simple task; namely, instructing Vera to match the A/C "Mode" to the current House Mode, and vice versa.My crazy construct involved arrays and indexes (to prevent illegal Mode values), lookup tables (to cross-reference integers (2) with words ("away")), time delays (to ward off bouncing/throttling), Global Expressions (to track device states and act as Triggers), boolean flags (to test parity and serve as a Constraint in Rules), etc., etc.
Think I'll stick with a simpler set of Rules, one for each case (Home on Vera ► Home on A/C; Away on A/C ► Away on Vera, etc.) and be done with it. MSR gives you so much more breathing room than the Vera environment, why not utilize it?
Sorry @rigpapa for the unnecessary back-and-forth above, and for not having the time required for ME to diagnose my own issue(s) last night.
-
Well, I think you hit on an important issue, which is that the "trial run" (of an individual action in a reaction) doesn't run in full context (the trial run of the full reaction, however, does). It may be that it can't (I can't imagine, it's really just a matter of if the squeeze is worth the juice), but I'm completely on board with the idea that it should. So I'll be looking into that today.
-
This post is deleted!
-
I honestly can't wrap my head around what you are telling me. It sounds as if you think the values
home
,away
, etc. are different from the strings "home", "away", etc. and somehow that's a problem, but all parameters in Vera are strings, even the numbers. There is no such construct as a "keyword" and no non-string special values forhome
oraway
etc. If you want to sendaway
to the Ecobee thermostat on the Vera, the only way to do it is by sending it a string.It sounds to me like you need to (a) look at the MSR log to see what MSR is sending to the Vera, and (b) look at the Vera LuaUPnP log to see what is being received and what it's doing with it.
-
This post is deleted!
-
This post is deleted!
-
Eesh, suddenly it is all working fine!! Please let's DELETE this convo and I promise never to mention it again. Something must've been wrong before the lunch break that was mucking up the workflow. SORRY!!
-
No problem at all... but please, do look at the log file on MSR to see what it is sending, because right now, I have ALL actions being logged on all platforms, regardless of log level settings, for just this very reason. If what's being sent makes sense, then just look on the Vera side to see what it is getting, which is hopefully the same, but that is literally looking at one side and the other of one function call (to do the HTTP request), so there should be parity. But, there can always be a case where something about the value isn't kosher... an extra space before or after? Something sneaking in that doesn't immediately jump out. Logs. Always the logs.
-
WILCO, just bear with me while I endure the "two steps back" phase of debugging my workflow, and get back to you.
For the moment, I suspect the problem is me, not MSR. -
@toggledbits I'm noticing that in MSR the following boolean test expressions are equating to FALSE when I naively expect TRUE (at least for the array version):
1 in {one:1}
and1 in [1,2,3]
Also FALSE for:
"one" in keys(testobj)
wheretestobj := {one:1,two:2,three:3}
which MSR sees internally as{"one":1, "two":2, "three":3}
More importantly, despite
in
being declared a reserved keyword in lexpjs, it is not (yet) explicitly included in your docs among the array operations. Should I submit a request for that?Thanks for your insights, as always.
EDIT: Think I found the answer to where "in" belongs in the lexpjs docs... it's intended for the
each..<item>..in..<array>
construct, whereas the docs currently describeeach..<item>..OF..<array>
so please accept this typo submission.I believe this means I should not be attempting to use
in
as I described above, then? And always useindexOf()
instead? -
@librasun said in Expressions and LuaXP Functions:
1 in {one:1}
This is false because 1 is not a key of the object.
1 in [1,2,3]
Is actually true (just tested in lexpjs) because 1 is a key in the object (i.e. the element at array index 1 exists). This is JavaScript semantics.
Also FALSE for: "one" in keys(testobj)
That would be correct because, following those JavaScript semantics,
keys()
returns an array, andone
is not a valid index for any array.Now, we can talk about whether or not the array behavior is expected. The smart folks that came up with JavaScript think so, but that doesn't mean it's the right answer for Reactor. It never occurred to me to use
in
on array in JavaScript, so I've never seen this effect; the "correct" way is (among several)Array.find()
orArray.indexOf()
. There is anindexOf()
function in MSR/lexpjs.On the
1 in { 'one': 1 }
being false, though, this is defensibly correct in both Reactor and JavaScript. If we change the behavior ofin
for arrays, then test (and true result) you want here would be written as1 in values( { 'one': 1 } )
-
I see. I hadn't considered the
.find
method only because it wasn't explicitly mentioned in lexpjs docs, but should have assumed it present from JS by extension.Furthermore, I stand corrected about
1 in [1,2,3]
which I swore (should not!) MSR returned as FALSE on first run of test. My bad.Lastly, where do you stand on
[1,2,3] || [3,4,5]
and[1,2,3] && [3,4,5]
? Former is returning TRUE (sorta expected) and latter is returning[3,4,5]
which I kinda get, but don't? -
toggledbitsreplied to LibraSun on Mar 16, 2021, 6:15 PM last edited by toggledbits Mar 16, 2021, 2:16 PM
@librasun said in Expressions and LuaXP Functions:
I see. I hadn't considered the .find method only because it wasn't explicitly mentioned in lexpjs docs, but should have assumed it present from JS by extension.
[referring to
Array.find()
] That's not a lexpjs function, it's how you do what you are trying to do in JavaScript (find a value in an array). Your Reactor/lexpjs solution isindexOf()
. Also note that dot-function notation is not supported by Reactor/lexpjs -- you can't saysomearrayname.indexOf( lookingfor )
, you have to writeindexof( somearrayname, lookingfor )
.I'm tempted to just leave
in
as-is for arrays, but well-documented, simply because it follows a common-use language pattern, and there is a defined replacement, which happens to be (almost) the same for both.Lastly, where do you stand on [1,2,3] || [3,4,5] and [1,2,3] && [3,4,5]?
That's a bug. The answer for the former should be
[1,2,3]
and for the latter[4,5,6]
. Fixing now. -
MSR SAMPLE EXPRESSIONS: Timestamp
If you want to display a timestamp in a stored variable or an outbound message from MSR, that looks like "
14:24:43 Wed 2021-Mar-17
", allow me to save you some typing. Just include this Expression in your Reaction, and reference${{timeStamp}}
in your workflow.daysList = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"], monthsList = ["","Jan","Feb","Mar","Apr","Jun","Jul","Aug","Sep","Oct","Nov","Dec"], timeNow = dateparts(time()), timeStamp = timeNow.hour + ':' + substr('0' + timeNow.minute,-2,2) + ':' + substr('0' + timeNow.second,-2,2) + ' ' + daysList[timeNow.weekday] + ' ' + timeNow.year + '-' + monthsList[timeNow.month] + '-' + substr('0' + timeNow.day,-2,2)
This construct takes advantage of the multi-statement feature built into MSR, which permits you to string together a series of declarations and evaluations within a single Expression. I've also sprinkled in a little formatting spice, so that single-digit
day
,minute
andsecond
fields display with a leading0
. -
toggledbitswrote on Mar 17, 2021, 8:23 PM last edited by toggledbits Mar 17, 2021, 4:29 PM
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. -
Does MSR have an equivalent of Reactor's...?
getattribute( DeviceNum ,'name')
Cannot find such an attribute in Expression Builder.
-
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.
37/126