For several versions now XTension has had the ability to average the most recent values for a unit without adding any code at all. In the Advanced tab of the Edit Unit Window you can select how many values to keep and average. If that does not meet your requirements or you need lower level control then read on for more info on scripting the changes to the value.
Averaging and throwing out exceptional readings
Sometimes you've got a temperature or other analog input that will get some bad data, or that fluctuates around the real value enough that it makes it difficult to make decisions based on the input.
This script will help to smooth out the data. What it does is to keep the last 10 readings (thats configurable, could be more if you need it) as it adds a value it walks a list of the last 10 readings, throwing away the highest and the lowest and then averaging what remains.
Installation is simple, cut and paste this script into the ON scrip of the unit you need to manage.
-- -- Average temperature handler -- james@sentman.com for Chester to get rid of goofy temp readings -- -- keeps 10 temp readins in a list in this unit's properties -- removes the highest and lowest of them and averages the remaining 8 values -- --you can change this number to change the number of elements that are averaged --larger numbers will provide more buffering set BufferSize to 11 -- get the list of data stored in our unit properties of the last 10 readings -- but inside a try block as it might not be there if this is a new unit set TheDataList to Get Unit Property "Data List" --handling lists can be a pain -- the Get Unit Property verb will not return an error but a blank string -- if the property doesn't exist yet, so we first need to check to see if it's -- an empty string and change it to an empty list -- and a list that has only 1 element may be converted to a simple string -- so if the variable is not a list, then we convert it to a list. if TheDataList is "" then set TheDataList to {} else if class of TheDataList is not list then set TheDataList to {TheDataList} end if end if --add new value to the end of the list --and remove the oldest if we've reached 10 values set NewValue to (future value) as number set the end of TheDataList to NewValue if (count of TheDataList) is BufferSize then set TheDataList to items 2 through BufferSize of TheDataList end if --resave back to the unit properties Set Unit Property "Data List" to TheDataList --walk the list and filter the biggest problem numbers prior to averaging. set SkipHighestIndex to 1 set Highest to item 1 of TheDataList set SkipLowestIndex to 1 set Lowest to item 1 of TheDataList repeat with i from 1 to (count of TheDataList) set ThisData to item i of TheDataList if ThisData is greater than Highest then set Highest to ThisData set SkipHighestIndex to i end if if ThisData is less than Lowest then set Lowest to ThisData set SkipLowestIndex to i end if end repeat --average the data leaving out anything at those indexes set Adder to 0 set ValueCount to 0 repeat with i from 1 to (count of TheDataList) if (i ≠ SkipHighestIndex) and (i ≠ SkipLowestIndex) then set ThisData to item i of TheDataList set Adder to Adder + ThisData set ValueCount to ValueCount + 1 end if end repeat --if we haven't added at least 1 value then we can't divide by zero, so just leave the currently set value --in that case. if ValueCount is greater than 0 then change future value to (Adder / ValueCount) end if
If you're testing this by using the set value verb instead of a real value you may notice that the log shows the value that you entered in the verb and not the processed value. The unit itself will reflect the processed value but in XTension versions to build 734 the log still shows the value as entered. Will sort that out in a future build.
Catch new values out of range and ignore.
Another technique : I use this simpler method to catch and 'shape' any rogue readings. Note also that this assumes a couple of other units for capturing the high and low temps for the day. (these are reset at about midnite each nite by a scheduled 'daily maintenance' global script… NOTE that I just toss an error and ignore the reading… (michael)
-- handle Temperature values from the DS2438 in the RFXSensor type 3 -- temp script to convert the values coming from the W800 or RFX. -- scale the 'future value' ... low 3 bits are = 0.125 each count set theTemp to (future value) if (future value) is greater than 1024 then set theTemp to theTemp - 2048 set theTemp to theTemp / 8 as integer write log "The temperature is " & theTemp & " Centigrade" set theTemp to (theTemp * 9 / 5) + 32 if theTemp < ((value of (thisUnit)) * 1.15) and ¬ theTemp > ((value of (thisUnit)) * 0.85) then change future value to theTemp write log "The temperature is " & theTemp & " Fahrenheit" if (future value) is greater than (value of "High Temp Today") then set value of "High Temp Today" to (future value) end if if (future value) is less than (value of "Low Temp Today") then set value of "Low Temp Today" to (future value) end if else change future value to (value of (thisUnit)) write log "Extraordinary temperature reading ... ignored" end if