Expression variable coercion and initial values

I have been lately playing more with expressions and here is one finding which may be useful to others.

There is an example where data is pushed to an array and then calculations can be done with the data:

In this example there is three main parts
-create interval condition which is then triggering expression
-push data to array
-do something with array values

So something like this

isint 
getstate( "Reactor Sensor 13", "urn:toggledbits-com:serviceId:ReactorGroup", "GroupStatus_root" )

series
if( isint=="1", push( series, tonumber(  getstate( "BR_Temperature", "urn:upnp-org:serviceId:TemperatureSensor1", "CurrentTemperature" )  ), 5), series)

range
max(series)-min(series)

The issue is that variables get initial value of null. So if now the interval driving isint is set for example to one hour, during the first hour series is null, range tries to get min and max out of null and that will result to coercion error. After the first push to array happens then everything works ok.

To fix this, an initial value can be used:

isint 
getstate( "Reactor Sensor 13", "urn:toggledbits-com:serviceId:ReactorGroup", "GroupStatus_root" )

temperature1
tonumber(getstate("BR_Temperature", "urn:upnp-org:serviceId:TemperatureSensor1", "CurrentTemperature"))

series
if(series==null, list(temperature1), if( isint=="1", push( series, temperature1, 5), series))

range
max(series)-min(series)

Now if the series is null, it will be initiated as an array with a value and range calculation works from the beginning.

Above issue is not limited to only arrays, but all variables. For example take this simple expression which tries to store minimum value:

mintemp
if(temperature1 < mintemp, temperature1)

Since the initial value of mintemp is null, the result is only error message “[luaxp]Invalid comparison (number<table) at 15”. The same fix can be applied:

if(mintemp==null, temperature1, if(temperature1 < mintemp, temperature1))

An improvement idea might be a box next to expression/variable where initial value could be given (number, string, array) :slight_smile:



One thing the expressions could be used is filtering. I have some data sources where data has some noise and needs to be filtered. Here couple examples I have been working on.
First collect data to an array:

temps
if(temps==null, list(outtemp,outtemp,outtemp),if(every5min=="1", push(temps, outtemp, 3) , temps))

low-pass filtering:

LPtemp
if(LPtemp==null, temps[1], if(every5min=="1",0.6*temps[1]+0.4*temps[2],LPtemp))

median filtering:

medtemp
if( (temps[1]<=temps[2])&&(temps[1]<=temps[3]), if(temps[2]<=temps[3],temps[2],temps[3]), if( (temps[2]<=temps[1])&&(temps[2]<=temps[3]), if(temps[1]<=temps[3],temps[1],temps[3]) , if(temps[1]<=temps[2],temps[1],temps[2])))

If the LuaXP had median function this would be much easier :wink:

1 Like

series = push( series, somevalue )

results immediately in a non-null array even if series is null on the first execution. It is the specific purpose of push() to create an array if its first argument evaluates to null.

The coercion error is expected, because you can’t add/subtract null from/to anything.

But this all seems much ado about nothing, as all the expressions function once there is one data element in the array. No complex initializations or exceptions are needed. Let it take an error on the first pass. Probably harmless. If not, just disable your action until the array is seeded and move on. It’s probably not going to stay that way for long, and once rolling, it’s probably never empty again.

I think this is a a very valid idea and, surely, would not be difficult to implement.

I get a similar issue trying to use an integer variable as the index of a choose statement in an expression. Thus X=choose(intvariable, …). intvariable is deliberately left blank in the definition and then incremented through successive iterations of a button press to choose a sequence of options. It throws an error as it is treated as null or string to start with. I would simply like to preset it as integer in the definition - ideally initiated as Zero but without a fixed definition of Zero.

I get the idea of “Just let it fail once and then all will be OK”, but actually it fails after every reboot/reset. This is just clumsy programming, causes unpredictable behaviour and should not be difficult to fix.