Math / Date Expressions!

This is a terrific addition to SharpTools! I was especially pleased to see all the array functions present, as arrays are something I’ve used frequently over the years. I’ll definitely be exploring that further in the near future.

Here’s hoping one day we’ll be able to use expressions as inputs in Device Actions as well, but it’s not enough of a priority for me to add a feature request just yet. There are a lot of great ideas on that list, and I don’t want to sound too needy. :joy:

1 Like

This is a great new addition to the platform…thanks for much for all the hard work!

I’ve been able to utilize the new expressions to turn a variable true at the exact minute of another variable by using an ‘Every Minute’ rule (A variable turns true every minute, which causes another rule to execute and run an expression that checks to see if the current time is equal to the time in the variable).

The expression logic is almost flawless – it works perfectly during the morning hours (great for an alarm clock) but fails in the evening (not so great for a ‘Wind Down’ routine).

[where $Night = “8:45 pm”]

duration = now() - date($Night, “h:mm a”)
minutes = round(durationAs(duration, “minutes”),0)
minutes == 0

It doesn’t work in the evening due to the date portion being off, so I’ve tried to concatenate the current date to the variable which only contains the time.

[where $Night = “8:45 pm”]

today = formatDate(now(), "YYYY-MM-DD ")
fulltime = concat(today, $Night)
duration = formatDate(now(), “YYYY-MM-DD h:mm a”) - fulltime
minutes = round(durationAs(duration, “minutes”),0)
minutes == 0

The issue is that it can’t convert the ‘now’ time to a number for comparison.

Thoughts on where I’m confusing this?

I’m using the date functions to check if it is 1 hour or later than sunset to trigger some light rules. I grab the sunset time from a virtual weather device and then do this:

today = formatDate(now(), "YYYY-MM-DD ")
fulltime = concat(today, $TimeSunset)
now() > addHours(date(fulltime, “YYYY-MM-DD h:mm A”), -1)

This sets a boolean variable to true/false and away we go.

Thanks to Josh for giving me a hand.

You can use Variables in device actions, so you can have the expression save the result to a variable and then use that in a device command.

@Zach_Imler has you pointed in the right direction.

Keep in mind what each function is doing and what the output is.

  • date(input, format) - takes a string input along with an input format string, parses the input, and outputs a date (in milliseconds)

  • formatDate(date, format[, locale]) - takes a date input along with an output format string and outputs a string in the specified format

So if you need to display a date, then formatDate() is likely going to be your friend. If you need to parse a string as a date so you can perform math / comparisons on it, then date() is likely what you need.

So in your third line where you’re trying to use fulltime to calculate a duration, you probably would want to parse fulltime with date() and then compare that directly with now() since those would both be dates in milliseconds:

today = formatDate(now(), "YYYY-MM-DD ")
fulltime = concat(today, $Night)
duration = now() - date(fulltime, "YYYY-MM-DD h:mm a")
minutes = round(durationAs(duration, "minutes"),0)
minutes == 0

An alternative would be to compare the ‘time’ string for each

nowTime = formatDate(now(), "h:mm a")
equalText(nowTime, $Night)

I was hesitant to respond to this as the vast majority of time that something is being done every minute, it could be rewritten as an event driven rule. There are exceptional cases, like this one may be, where you want to check for a specific time and have a top-level IF Condition determine if the rule should run and that’s fine. But I would otherwise caution people against running rules every minute and instead feel free to create a thread where we can help see if there’s an event driven approach that works better.

Yes, I’m well aware of the “use a variable” solution to everything around here, but it’s inefficient and, in the case of larger rules, can be unwieldy. I’m simply advocating for streamlining rule creation, in this instance by allowing expressions as input in Device Actions.

Perfect, yes, both of those work great! I keep confusing the ‘formatDate’ and ‘date’ functions, thanks for setting me straight.

I’ll start another thread based on an event-driven approach.

Thx

I’m rewriting several rules using Math Expressions. The original rules were three screens long; the revised rules are one-third of a screen long. And they run so much faster!

This feature was in incubation a long time, but well worth waiting for!

1 Like

I am trying to set a rule that sets a variable ($TomorrowDate) to a value of tomorrows date.

My Idea:
Trigger: Daily at 5:00pm

Flow: Set $TomorrowDate, with tomorrows date. (format = MMM-DD-YYYY)

I cannot seem to come up with the correct syntax to accomplish this. Essentially, the syntax would be Todays date + 1 day = Tomorrows date, in the format above.

I would appreciate any guidance on this.

You could use this:

1 Like

@Jason_K_Jennings, thank you! Exactly what I was looking for.

I had the “now” and addDays, mixed up. Didn’t think to swap around.

The result I was getting with my method was YYYY-DD-MM followed by the current time.

Is there something about the validation that has changed from the beta?

When I insert a number inside a context variable, I now get an error:

However, I had several of them working previously (see below). If I edit the one below, I get an error as well (which is at the zero location):

One of the last features added to the beta was validation of the expression when saving. It doesn’t attempt to evaluate the expression since some things like context variables aren’t available, but it does attempt to parse the expression.

The parser is probably getting hung up on the dot notation used for accessing a numerical index. You can use the full bracket notation to access a numerical index in an expression:

$context.response.data.hourly[1].weather[0].description

We pushed an update today to better support the legacy index dot notation with variables in the Expression editor.

I would still recommend using the bracketed index format as the rest of the expression parser/evaluator requires that format.

Is there a way to manually set a time in a variable and then convert that the an Epoch time?

My goal: set a text variable to time of choice (EX. 6:00 am) then convert that to Epoch time for calculations in another rule.

Most of the date functions return the timestamp in milliseconds since the Unix epoch. You could store that value directly in a text or numeric variable.

Since ‘epoch time’ is the seconds or milliseconds since the ‘unix epoch’ (January 1st, 1970 at 00:00:00 UTC), keep in mind that a particular time of day (eg. “6:00 AM”) isn’t something that can be directly expressed in ‘epoch time’. A specific time on a specific day is something that could be expressed in ‘epoch time’ though… so I suppose it depends on exactly what you’re attempting to do.

If you have a particular time stored as a string, an approach was discussed in some detail above, so I would recommend reading that post: Math / Date Expressions! - #5 by josh

Perhaps I am not finding it, but it seems that I can’t use math in a Rule action. For example, {{$HeatingTemp}}-6. I like my house in a specific range of temps, but due to dead zone limitations on the thermostats, I have to first move the opposite setpoint far enough away from the intended setpoint. In this example, I need the heating setpoint variable reduced an additional 6 degrees.

1 Like

The inline expressions are available within text parameters, but are not currently available in numeric parameters of commands. You can create a variable and use an expression to set the numeric variable though.

image

Inline Expression Example (tap to expand)

For reference, here’s what the ‘inline expression’ equivalent would look like with a text parameter. Note that the whole expression goes within the double squigglys:

image

This is such a n00b question (clearly I haven’t RTFM), but I’m curious:

  1. Are the available Variables only local to the app, ported from the hub, or a mixture of both?
  2. If Hub Variables are indeed in play (and I seem to recall they are, without resorting to Variable Connector devices, which is sweet!), then is it safe to assume their value gets updated as new values get assigned to them in SharpTools? (i.e. without employing a separate setVar action)

Thanks for posting @LibraSun - the variables referred to here are SharpTools Variables. You can find more information about them in the following article:

SharpTools variables are a way to store data like Text, Numbers, and True/False variables. These are not tied to any one platform (eg. Hubitat) and can be used in dashboards and rules.

Context Variables are also supported, which provide context about the event that triggered the rule. For example, you might react to the mode changing in Hubitat and use that to set the mode in SmartThings. Or when a temperature changes, you might use that value directly in a calculation.

As for Hubitat variables, you would need to use the respective Connector device. You could map these into SharpTools Variables or use their events via Context Variables as alluded to above.

Awesome sauce, and thanks for the explainer. Everything you guys have done is super impressive, and only getting better. Thanks for the link!

Full disclosure: My favorite part of any animation environment has always been the variables and math section, so all this wets my whistle.