Trigger an Activity on a change of Variable?

for some reason i cannot get this to work correctly and i know there is a trick that i am missing. i’ve created a reactor to hadle my notifications in a consistent way. that reactor has a variable “input” that is exported. on other reactors, i set that variable (via a davice action → set variable) and then the notification reactor does it’s thing and messages, texts, etc. depending on the alert level. the issue I have is on repeated notifications, since the input string doesn’t really change, the reactor does not trigger a new alert. this is what i see in the logic summary:

Events
    2020-05-10 10:59:32: Reactor startup (Luup reload)
    2020-05-10 10:59:32: Starting (Luup Startup/Reload)
    2020-05-10 10:59:33: Sensor update starting
    2020-05-10 10:59:33: Sensor update completed; 0.049s
    2020-05-10 10:59:58: Variable "input" set to "[\"notification\",\"this is a notify message\"]"; was "[\"notification\",\"this is a notify message\"]"
    2020-05-10 10:59:59: Sensor update starting
    2020-05-10 10:59:59: Sensor update completed; 0.034s
    2020-05-10 11:00:11: Variable "input" set to "[\"notification\",\"this is a notify message\"]"; was "[\"notification\",\"this is a notify message\"]"
    2020-05-10 11:00:12: Sensor update starting
    2020-05-10 11:00:12: Sensor update completed; 0.035s

i tried using a device state condition on the “input” variable (using the this Reactor Sensor as the device), but it doesn’t seem to pick up changes. If i look at the variable in the condiftions tab, it shows “Current value: (not set)”, though in the variables tab it is set.

Logic detail below:

```
*************************************************** REACTOR LOGIC SUMMARY REPORT ***************************************************
   Version: 3.6develop-20128 config 20070 cdata 20045 ui 20085 pluginDevice 181 LuaXP 1.0.2enh
    System: Vera version 1.7.4970 (7.31) on Sercomm G450 ID 36 (Vera Plus); loadtime 1589122743/1589122772; systemReady 1589122777; ALTUI v2.49
       Env: Lua 5.1; JSON dkjson 2.5+LPeg; UnsafeLua=nil/true
Local time: 2020-05-10T11:00:19-0400; DST=1; Nags Head, North Carolina United States; formats %d/%m/%Y %H:%M:%S
House mode: plugin 1; system 1; tracking on
  Sun data: {"source":"int","civdawn":1589103150,"nautdawn":1589101102,"sunset":1589155005,"nautdusk":1589158742,"stamp":2020131,"latitude":35.858629,"astrodusk":1589160934,"longitude":-75.570285,"civdusk":1589156693,"astrodawn":1589098910,"sunrise":1589104838}
  Geofence: not running
        RS: ,,1589071548,1589071868,1589071950,1589072676,1589072774,1589073781,1589122772
        NS: 0:X,1589067360:U
************************************************************************************************************************************
RS-NOTIFICATIONS (#187)
    Version 20045.81 05/10/20 10:56:00
    Message/status: Not tripped
    Variable/expressions
       0: input                     [last "[\"notification\",\"this is a notify message\"]"(string)] (exported)
       1: convertedInput           if( len(input) > 0,  unstringify(input), "BLANK") [last ["notification","this is a notify message"](table)]
       2: alertLevel               if( len(input) > 0, upper(tostring(convertedInput[1])), "BLANK") [last "NOTIFICATION"(string)]
       3: theMessage               if( len(input) > 0, upper(tostring(convertedInput[2])), "BLANK") [last "THIS IS A NOTIFY MESSAGE"(string)]
       4: allowNotifications       true [last true(boolean)]
       5: allowDebug               true [last true(boolean)]
    Condition group "Alert Handler" (NUL)  false as of 22:41:13 <root>
      Z-F-group "NEW ALERT" (AND)  false as of 10:27:33 <grprgfkss4>
      |     &-F-service (self) (-1) urn:toggledbits-com:serviceId:ReactorValues/input update  [1589122173 => 1589122745 at 10:59:33; F/F as of 10:49:36/10:49:36] <condrhcu5qf>
      |     &-F-var alertLevel change ,Alert [blank => notification at 10:49:34; F/F as of 10:28:00/10:28:00] <condrgnot3d>
      Z-F-group "NEW NOTIFICATION" (AND)  false as of 10:49:36 <grprgfmfds>
      |     &-T-var allowNotifications = true [true at 22:28:24; T/T as of 22:28:24/22:28:24] <condrgna221>
      |     &-F-service (self) (-1) urn:toggledbits-com:serviceId:ReactorValues/input update  [1589122173 => 1589122745 at 10:59:33; F/F as of 10:49:36/10:49:36] <condrhcv8b2>
      |     &-T-var alertLevel change ,Notification [blank => notification at 10:49:34; T/T as of 10:49:34/10:49:34] <condrgfmgc1>
      Z-F-group "DEBUG MESSAGES" (AND)  false as of 22:31:26 <grprgncqct>
      |     &-F-service (self) (-1) urn:toggledbits-com:serviceId:ReactorValues/input update  [1589122173 => 1589122745 at 10:59:33; F/F as of 10:49:36/10:49:36] <condrhcvwjx>
      |     &-F-var alertLevel change ,Debug [blank => notification at 10:49:34; F/F as of 22:45:53/22:45:53] <condrgnqdv6>
      |     &-T-var allowDebug = true [true at 22:31:26; T/T as of 22:31:26/22:31:26] <condrgncsy6>
    Activity grprgfmfds.true
        Notify method SM nid 3: users  message "{ \"REACTOR NOTIFICATION \\n=======================\\n\\n MESSAGE:\\n\" + theMessage + \"\\n\\n————————————————\\nTime: \"  +  strftime('%H:%M:%S   %m-%d-%Y')   }"; SMTPServer="smtp.gmail.com"; SMTPPort="465"; SMTPSender="REACTOR <my@email.com>"; SMTPDefaultRecipient="REACTOR <my@email.com>"; SMTPDefaultSubject="REACTOR EMAIL"; SMTPUsername="my@email.com"; SMTPPassword="****"; SSL opt {"verify":"none","mode":"client","protocol":"tlsv1"}
    Activity grprgfkss4.true
        Notify method SM nid 2: users  message "{ \"REACTOR ALERT \\n=======================\\n\\n MESSAGE:\\n\" + theMessage + \"\\n\\n--------------------------------\\nTime: \"  +  strftime('%H:%M:%S   %m-%d-%Y')   }"; SMTPServer="smtp.gmail.com"; SMTPPort="465"; SMTPSender="REACTOR <my@email.com>"; SMTPDefaultRecipient="REACTOR <my@email.com>"; SMTPDefaultSubject="REACTOR EMAIL"; SMTPUsername="my@email.com"; SMTPPassword="****"; SSL opt {"verify":"none","mode":"client","protocol":"tlsv1"}
        Notify method SM nid 4: users  message "{ \"REACTOR ALERT: \" + theMessage + \"   Time: \"  +  strftime('%H:%M:%S   %m-%d-%Y')   }"; SMTPServer="smtp.gmail.com"; SMTPPort="465"; SMTPSender="REACTOR <my@email.com>"; SMTPDefaultRecipient="REACTOR <my@email.com>"; SMTPDefaultSubject="REACTOR EMAIL"; SMTPUsername="my@email.com"; SMTPPassword="****"; SSL opt {"verify":"none","mode":"client","protocol":"tlsv1"}
    Activity grprgncqct.true
        Notify method SM nid 5: users  message "{“debug message: “ + theMessage}"; SMTPServer="smtp.gmail.com"; SMTPPort="465"; SMTPSender="REACTOR <my@email.com>"; SMTPDefaultRecipient="REACTOR <my@email.com>"; SMTPDefaultSubject="REACTOR EMAIL"; SMTPUsername="my@email.com"; SMTPPassword="****"; SSL opt {"verify":"none","mode":"client","protocol":"tlsv1"}
    Events
        2020-05-10 10:59:32: Reactor startup (Luup reload)
        2020-05-10 10:59:32: Starting (Luup Startup/Reload)
        2020-05-10 10:59:33: Sensor update starting
        2020-05-10 10:59:33: Sensor update completed; 0.049s
        2020-05-10 10:59:58: Variable "input" set to "[\"notification\",\"this is a notify message\"]"; was "[\"notification\",\"this is a notify message\"]"
        2020-05-10 10:59:59: Sensor update starting
        2020-05-10 10:59:59: Sensor update completed; 0.034s
        2020-05-10 11:00:11: Variable "input" set to "[\"notification\",\"this is a notify message\"]"; was "[\"notification\",\"this is a notify message\"]"
        2020-05-10 11:00:12: Sensor update starting
        2020-05-10 11:00:12: Sensor update completed; 0.035s
    Watches
        Device #187 RS-NOTIFICATIONS service urn:toggledbits-com:serviceId:ReactorSensor variable TestHouseMode
        Device #187 RS-NOTIFICATIONS service urn:toggledbits-com:serviceId:ReactorSensor variable cdata
        Device #187 RS-NOTIFICATIONS service urn:toggledbits-com:serviceId:ReactorSensor variable TestTime
1 Like

EDIT: This was poor/incomplete advice. See reply immediately below…

“Be sure your Condition uses [Updates] instead of [Changes] so that Reactor knows to fire “true” again even if the same value gets written into your watched variable.”

1 Like

Clever, but you’ve got at least one logic error: the logic conditions in the “NEW ALERT” group and “NEW NOTIFICATION” group that check alertLevel specifically check for a change from blank to the level you’re looking for, but if the level is not blank and already at the level you’re trying to send a notification for, that condition is false (it gets reset because it hasn’t changed from blank to NOTIFICATION"–it stays at NOTIFICATION on the next evaluation). You need to either use different logic there, or you need to actively reset alertLevel back to blank each time you send a notification.

In other words, if you send two notifications at NOTIFICATION alert level in a row, it’s deaf to the second one (and maybe the first, if the one before that was also at NOTIFICATION alert level), because of the way your changes test is structured. You need to fix that. Since the group is AND, as long as that condition is false, no activity will run, no notification will go out.

Edit: oh, and unfortunately, update will only work if the value of input changes. The only state variables in Vera for which update will trigger when written with the same value are the special variables for locks and scene controllers (sl_SceneActivated, sl_UserCode, etc.; they generally start with sl). You can work around this issue by including the timestamp when setting input, for example. Unless you are sending a lot of notifications quickly (which this approach isn’t really suited for), that should be sufficient to cause a change on every write.

1 Like

@rigpapa, thanks for the tips. i knew the logic was flawed for the notifications as i was testing a bunch of different options, my question was more around the update trigger which you answered. i do append the timestamp on this reactor sensor (the one that does the notification), so i will just move that out to the sending reactors, i was just trying to make the call to this one as simple as possible.

Speaking of passing the time, in the “set variable” command, for the life of me i can’t get it to append the time to the array i am passing. i’ll muck around with it some more today, but may have some more questions later. Appreciate the help!

1 Like

ok, got it mostly working now by using a timestamp on my notification reactor since (i think) it only re-evaluates whenever something changes in the reactor. low tech, but functional.

@rigpapa, can the activities → device action → set variable → NewValue location take a full expression, or just does variable substitution? the way i read it in the docs is the latter, but i may be wrong. Any combination of { } and text, outside of an actual variable in the reactor just gets parsed as a string. My hope was to be able to use something like your stringify() function in there so i could create dynamic messages.

1 Like

The Luup/UPnP SetVariable device action in 3.6 will take an expression.

1 Like

I’m on 3.6, but most likely i’m using it incorrectly. I guess my question is how do you pass an array of strings using stringify()? do i need to escape out all the double quotes in the array?

Ah, OK. I think stringify() is not going to help you, because you need to stringify on the sending end, not the receiving end. By the time you’ve invoked the SetVariable action, it’s too late (the variable substitution occurs on the receiver, not the sender). Alternatives:

  1. Load dkjson and use it to encode the string you pass to SetVariable:
json = require "dkjson"
payload = [ "notification", "I am ready to go!", time() ]
luup.call_action( "urn:toggledbits-com:serviceId:ReactorSensor", "SetVariable",
    { VariableName="input", NewValue=json.encode(payload) }, devnum )
  1. Use a simpler structure for input. For example, instead of building an array in JSON format (which is basically what you’ve been doing), just make a delimited string using an odd character that won’t appear in your messages, like “|”. On the sending side:
messageText = "whatever you want to send"
payload = "notification|" .. messageText .. "|" .. time()
luup.call_action( "urn:toggledbits-com:serviceId:ReactorSensor", "SetVariable",
    { VariableName="input", NewValue=payload }, devnum )

… and then on the receiving side, the expression to convert the received value to an array:

convertedInput expression: split( input, "|" )

Everything else should be OK the same.

2 Likes

welp, that’s what was missing, i went with the simple structure as it makes it clean on the notifications. thank you so much, if i could give a 1000 likes i would

1 Like

One more question… is there a way to return an array from one of the expressions? i had a bunch of these notifications built, but in a couple i forgot to append the time variable, so the notifications would error out. i was thinking about parsing the input (“message”) and wrapping it in an if statement in case i didn’t pass enough parameters like this:

if( find(message, “|” ) == 2, split( message, “|” ), [“ERROR”, “Error in parameters”, time()] )

but that seems to break giving me an error of “[luaxp]Unexpected end of array subscript expression at 9”. My guess is that luaXP is only working on strings and not objects. i can set another variable with the error, but it would be simpler/cleaner if i could set the array. no biggie, just thought i’d ask.

Try:

if( find(message, "|" ) == 2, split( message, "|" ), list("ERROR", "Error in parameters", time() ) )

Let us know if that does what you need?

Oh, and always wrap any code with 3 backticks ``` before and after – as I’ve done here – to preserve formatting and to avoid lethal “pretty” quotes (note how I manually edited them to be straight quotes that Vera expects) that can render your code unusable when cut-and-pasted.

2 Likes

thank you! thank you! thank you!

worthy

1 Like