Rolling averages of device data?

Hey There - Im new to SharpTools coming over from WebCoRE after ST gave them the boot. Im still slowly getting up to speed on some of the differences and would not consider myself a programmer but one thing I have not figured out is how to get a rolling average. One of the main things I used WebCoRE for was for creating a 15 min rolling average of a solar cell voltage device I built that I was using as a proxy for sunlight intensity for control of my shades to avoid constant up and down actions on partly cloudy days. Essentially, it polls the device for voltage every 30 seconds and generates a 15 min rolling average of the 30 data points. If anyone has any ideas on how I might migrate this WebCoRE piston to SharpTools, I would greatly appreciate it. Thanks!

How often is the device reporting the voltage to SmartThings?

If it’s already reporting every 30 seconds, you can use the value changing as your trigger and then use expressions to store and calculate the average.

The device itself is only reporting to ST when the value changes.

How often does the value change? I’m wondering if you could still do it when the value changes and do your average differently.

Im certainly open to do it another way. The value change frequency varies quite a bit, really just depends on the weather. Sometimes it’s every few minutes sometimes it’s half an hour. Here’s an example from yesterday during peak sun hours:

I believe some of the advanced Expression features could help you here.

Here’s one approach that comes to mind:

  • Store an array of events and add the newest event to the list
  • Filter the array of events to just those that occurred in the last 30 minutes
  • Average the list of filtered events

The main expression for managing the list would look something like the following, where we are setting an $AverageList text variable using an expression.

image

Then immediately after that we would set a $AverageNumber numeric variable with an expression:
image

These are using a bunch of advanced expression features, so feel free to ask questions. :smiley:

Text Version for easier copy-paste (tap to expand)

Set $AverageList to expression:

#// decode the object
list = parseJson($AverageList) 

#// if it's empty, initialize it
list = isEmpty(list) ? [] : list 

#// add our new event to the list
list = concat(list, [{"ts": now(), "value": $context.event.value}]) 

#// filter the list
list = filter(list, x.ts > addMinutes(now(), -30))

#// last step always needs to stringify the content for the result
stringify(list) 

Set $AverageNumber to expression:

list = parseJson($AverageList)

#// calculate the average of the 'value' property within the items
mean(map(list, x.value))

So your final rule might look something like:

Immensely helpful - thank you! I’ll give this a go this afternoon. Thanks again!

1 Like

My pleasure! I just edited the post with a slightly simplified version.

I originally thought I would need to store the last event separately as a fallback in case the filtered list was empty, but then I realized that since things were event triggered we would always have at least one item in the filtered list.

You can always tap the pencil icon to view the old version if you’re curious about the changes:

So far so good. Brilliant. Thanks again!

1 Like

This working great - Thanks again for the novel thinking on this. I have another question unrelated to the averaging but in the same rule I’m writing for the shade operation. Am I understanding this correctly: In the image below, this condition is saying that if any of these shades are not closed then ___. I have several of these shades in groups for a number of different scenarios and would only like the action to execute if the state is not correct for a given group, i.e. IF any of shade 1, 2, or 3 is not open THEN open shade 1, 2, and 3. I have a lot of these conditions to make and just want to make sure I’m understanding this correctly. Thanks!

That looks like it’s within an AND condition (ALL), so it would require all of the shades to be ‘not closed’ for it to go down the THEN path.

“Attribute ‘windowShade’ from all of Shade 1, Shade 2…”

The Multi Device conditions match whatever their enclosing IF Condition is configured for, so depending on the rest of the rule configuration, you might be able to change the IF Condition from ‘all’ to ‘any’.

image

  1. Tap the Edit button in the bottom of the IF Condition
  2. Change from ‘any’ to ‘all’

Thanks Josh - This is part of a complex IF where I have three or four devices device states that must be matched in addition to the this condition for the shades I’m looking for. Is there another way I might be able to create a variable for each of these shade groups somehow. Im thinking maybe a boolean for $ShadeGroup1OPEN and if all are open in that group then TRUE. Same for CLOSED. Again this is a non-programmer spitballing here :slight_smile: open for suggestions.

Yes, you could set a variable using an IF Condition. Depending on your rule, you might be able to do this just before your main IF Condition block… or it might make sense to just setup a separate rule dedicated to this.

This handles just the ‘all on’ concept. Depending on your needs, that single variable may be enough (eg. it’s true if they’re all on… so if it’s false, at least one is off.) Otherwise you could add another IF Condition within the rule for the ‘any off’ - for efficiency, it would make sense to be nested within the ELSE path of the first condition (since it can’t be true if they’re all on and otherwise is wasting queries), but you could just do another IF condition after the first one for visual simplicity.

I would also add that sometimes it’s just easier to send the command irrespective of the current state of the device. For example, sending on() to a device that is already on generally doesn’t hurt anything. Same with telling the shades to open() if they’re already open - generally the device will just ignore the command since they’re already open.

I think I will try this route with the variables. I had initially set the rule to send the command regardless but the trigger frequency and number of other devices that were also being triggered by routines in SmartThings resulted in a lot of congestion in the Z-Wave transmission, sometimes taking 5 minutes or more to fully execute. Still trying to figure out all of the latency issues but think this will help in the meantime. Thanks Josh!

1 Like