Lambda function EOF and other errors

Im getting an EOF error in a lambda function. I dont know what its trying to tell me. I cant be sure Ive got te format right. Im finding various examples and none seem to work.

Im using version 2.2

rules

val Functions.Function4 occ=[Start,Stop,Time,Proxy |
        if(Time > Start && Time < Stop){
                Proxy.sendCommand(ON)
        } else {
                Proxy.sendCommand(OFF)
        }
        logInfo("Schedule","Change "+Proxy+Time.state)
        ]

Start , Stop and Time are values in millis ( I know the if state works as Ive done it outside of the lambda),
and Proxy is the switch item I want to set the state of.

error

2018-01-23 14:42:51.644 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model 'occupancy.rules' has errors, therefore ignoring it: [17,35]: mismatched input ',' expecting ']'
[18,21]: no viable alternative at input 'Time'
[18,32]: missing EOF at ')'

just for reference, 17,1 is the first charactor of the 'val’
And I did only get the EOF error to start with, but then read that on >openhab2 you dont need to import the class and you dont need to specify the item types. Now im not too sure…

Thanks

Read this https://community.openhab.org/t/reusable-functions-a-simple-lambda-example-with-copious-notes/15888

  1. you have to define the types of input arguments
  2. there must be a return argument

I did read that. Thats where I read that you dont need to define the types.

The return argument, I read somewhere you can just put true at the end. But that doesnt change the error.

Ive changed it to the following but get the same error

import org.eclipse.xtext.xbase.lib.Functions

val Functions.Function4<Number,Number,Number,GenericItem> occ=[Start,Stop,Time,Proxy |
        if(Time > Start && Time < Stop){
                Proxy.sendCommand(ON)
        } else {
                Proxy.sendCommand(OFF)
        }
        logInfo("schedule_function","Change "+Proxy+Time.state)
        true
        ]

Try this :slight_smile:

val Functions$Function4<Number,Number,Number,GenericItem,Boolean> occ = [ 
  Start,Stop,Time,Proxy |
        if(Time.state > Start.state && Time.state < Stop.state){
            Proxy.sendCommand(ON)
        } else {
            Proxy.sendCommand(OFF)
        }
        logInfo("schedule_function","Change "+Proxy+Time.state)
      true
]

Still get the same errors.

I have a few questions though.

-val or var? I read val is for a value you define and dont change. How does that apply here with a function that is used by different variables with different outcomes?

-boolean? does it need to be defined?

-indenting? to make things easier to read can you seperate the line across multiple lines and not worry about indenting?
The indenting of the if statement, is that important in the rules? how do you indent the if state and split across multiple lines without causing errors?

Thanks

I did not say that in that posting anywhere. In the first example, you can clearly see that the Type is supplied and required when you don’t use generics (i.e. the < >) in the list of arguments.

My example:

import org.eclipse.xtext.xbase.lib.*

val Functions$Function1 log= [ GenericItem s |
    logInfo("lambda", s.state.toString)
    s.state.toString + " logged"
]

Your original example fixed:

val Functions.Function4 occ=[Number Start, Number Stop, Number Time, GenericItemProxy |
        if(Time > Start && Time < Stop){
                Proxy.sendCommand(ON)
        } else {
                Proxy.sendCommand(OFF)
        }
        logInfo("Schedule","Change "+Proxy+Time.state)
        true // logInfo returns void so it cannot be the last line of a Function
]

Or, using the new syntax where I stated in the lambda tutorial:

If desired and if you do not want to have to cast things, particularly the return type, you can use the following syntax.

That does not mean specifying the Types is optional.

You are never assigning a new Functions$Function4 to occ so occ remains unchanged. Therefore val is the appropriate way to declare it. If you ever have

occ = // something else

Then you would use var.

If you list the argument types, you must list the type of the return value as well, which is what the Boolean denotes.

Indenting is there for the human. The Rules do not care about them so you can indent however you want to.

But I will caution you. The purpose of the indenting is, like I said, for human readability. Standard convention is to always indent when defining a new context, and going back when the new context ends.

New contexts are defined in the Rules DSL using { } and [ ]. So, as you can see in Kevin’s example:

  • a new context gets started with [, every line after that is indented.
  • the next context, the if, is started with a { and everything after the { is indented further
  • the if context ends with the } which is unindented.
  • eventually we get to the ] which closes out all the new contexts.

If you have something that gets split across multuple lines the standard is to start the new line at the same indentation as the previous line so it lines up and is obvious that the new line is in the same context as the previous one. For example:

    if(Time.state > Start.state &&
       Time.state < Stop.state) {
        Proxy.sendCommand(ON)
    }

If you deviate from this style of indentation too much and ask for help on this forum, you will be asked to correct it before we can help. Unindented code, or improperly indented code is exceptionally difficult to read and understand. Minor deviations are OK (for example, Kevin uses extra indentation for the body of the lambda).

One last note. If you don’t have a meaningful return value for your Function, pay attention to the note on the lambda tutorial:

Note: If you do not want to return something from your lambda, you should use Procedures$ProcedureX where X is the number of arguments.

So

val Procedures$Procedure4<Number,Number,Number,GenericItem> occ = [ 
  Start,Stop,Time,Proxy |
        if(Time.state > Start.state && Time.state < Stop.state){
            Proxy.sendCommand(ON)
        } else {
            Proxy.sendCommand(OFF)
        }
        logInfo("schedule_function","Change "+Proxy+Time.state)
]

Thanks, Rich, I see your points.

I misread the type definition and assumed that meant the type would resolve itself.

I get the use of the indentation, but Im looking for reasons as to why I got the errors, and one of those errors mention the ‘|’ symbol which is next to an end of line.

On the final point, I tried the format you have corrected and still got the same errors.
I have now got rid of the lambda and written the same code over and over again for each schedule and this works. Ill leave it like that for now. I know this isnt the ‘right’ way to do it but I know it works.

Seperately, I have tried another lambda , this time following your post to try to avoid issues, and still get the same errors.

Can any one explain this error

2018-01-24 18:49:16.151 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model 'heatingdemands.rules' has errors, therefore ignoring it: [18,7]: mismatched input ',' expecting ']'
[19,53]: missing EOF at ')'

with regards to this rule

val Functions.Function4<Number,Number,Number,GenericItem> demand=[
                Temp,Setpoint,Deadband,Item|
                        if(Temp.state < (Setpoint.state - Deadband.state)){
                                if(Item.state!=ON){
                                        Item.sendCommand(ON)
                                }
                        } else if(Temp.state >= Setpoint.state){
                                if(Item.state!=OFF){
                                        Item.sendCommand(OFF)
                                }
                        }
        true
        ]

the errors are referring to 18,7, (second line) Temp,Setpoint
and 19,53, (third line second ‘)’ ) Deadband.state)){

thanks

The error usually indicates you are missing a closing ), }, or ]. But in this case it is because you are using a Type as a variable name. (see below)

You should be using VSCode with the openHAB extension. It would immediately find this error and point it out.

Unlike both my example above and Kevin’s example above AND my explicitly stating “you must list the type of the return value as well”, you are still failing to define the type for your return value.

If you have a Functions$Function4 (note the $, not the .) you must have 5 types listed in between the < > with the last one being the type of your return value.

Kevin’s example:

val Functions$Function4<Number,Number,Number,GenericItem,Boolean> occ = [ 

Your example:

val Functions.Function4<Number,Number,Number,GenericItem> demand=[

Besides the name of the lambda, there are two glaring differences. Kevin’s uses $ instead of . between Functions and Function4, and Kevin’s includes Boolean to define the type of the return value.

I cannot emphasize strongly enough how much these sorts of details matter. I don’t just type crap willy-nilly. I expect what
I write to be read and followed. It is impossible to provide assistance if you are ignoring half of what I write.

I expect you to compare your code with the multiple examples provided in this thread and identify differences. It does us no good to provide examples if you won’t look at them.

Other problems I see:

  • You are passing in a Number but trying to use it as a NumberItem. They are not the same thing. You need to pass them in as NumberItem.

  • Item is reserved word. It is a Type itself. You cannot use them to name your variables passed into the lambda. The error you saw above is because the language is looking for a variable name following Item. It gets to the end of the lambda before finding a valid variable name so it crashes parsing the rule.

  • You often have to help tell the rules engine that a state is a Number to perform comparisons like > or do math with them.

Correcting these problems, all of which were pointed out by VSCode appears to correct all the errors.

val Functions$Function4<NumberItem,NumberItem,NumberItem,GenericItem,Boolean> demand= [Temp, Setpoint, Deadband, i|
                        if(Temp.state < (Setpoint.state as Number - Deadband.state as Number)){
                                if(i.state!=ON){
                                        i.sendCommand(ON)
                                }
                        } else if(Temp.state >= Setpoint.state as Number){
                                if(i.state!=OFF){
                                        i.sendCommand(OFF)
                                }
                        }
        true
        ]

Im sorry I missed a few things there.

I would like to say though that Ive seen a lot of examples where people have marked them as solved but not mentioned things that you have here. I also struggle to find any real details about how a lot of this SHOULD be done, other than failing to work it out, asking questions, failing again, giving up, then annoying people because I didnt read something.

Its frustrating me because I cant just find the information. This is the first time that I have read someone explicitly detailing the use of $ instead of . in the function and I have read examples using both, and the first time ive seen ‘NumberItem’ and ‘as Number’ in general and with regards to in a lambda.

I dont know what VSCode is, Ive seen it mentioned once, (now twice), I havent seen it in the tutorial (unless I didnt read that too) so it wouldnt be something on my to do list, I wouldve thought the tutorial provides the info you need. And if VSCode was something it said I needed already I probably wouldve used it.

Im writing these in nano on a headless raspberry pi,. So far Ive not read anywhere that I should or shouldnt be doing that.

I would be very interested in seeing the examples that use . so I can point out to future readers that it is wrong. It will not work with ., it must be $. But the fact remains that all the examples provided in this thread and the links pointed to from this thread use $.

That part isn’t something I would have necessarily expected you to know off hand. And that is an issue separate from the lambda.

https://docs.openhab.org/configuration/rules-dsl.html#conversions though might be informative.

https://docs.openhab.org/configuration/editors.html

If the only thing you are using is the Beginner’s Tutorial you are missing the vast majority of the documentation. You should also be reading and searching through the User’s Manual.

https://docs.openhab.org/introduction.html

In particular, read the Concepts and Configuration sections.

Thanks, I do appreciate your help. Im finding this to be a much bigger learning curve than I was expecting. I started (and finished) programming an arduino with no programming knowloedge to do my heating system and that has be running and logging for over two years. Openhab seems so much harder and most of the work is already done for me. I think its the struggle to find the right information before it annoys me. Hence why I avoided a lambda before, and why I reverted to writing programs in python to do the job I couldnt work out with openhab and send the values over.

Anyway, back to the grind.

Ive been through your posts and Im certain I have this written out correctly. I copied your version into the file and compared the two and they are identical.

I find that importing the library or not, makes no difference to the error I get. Using a $ or . in the function makes no difference either. Which I why I didnt worry about it in the first place. I couldnt see any reason that it was issue.

I get a new error now though.

2018-01-25 15:00:00.185 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'OAT Hi check': Could not invoke method: org.eclipse.smarthome.core.items.GenericItem.getState() on instance: 1

So now I have different questions.

How come Temp.state is ok as a value to compare but Setpoint.state as Number is required? Why not Temp.state as Number? all of the values being compared are defined as the Number type.

The error mentions instance 1. Is that the instance of the function, or the 1st argument of the function? ie should my first argument actually be a GenericItem (it is declared in an items file).
The item was currently null so I set it to an OFF value and restarted but still got that error so Im a bit stuck again.

Home automation is not easy. The differences between programming a microcontroller to do something versus a platform that interfaces with 300+ separate and incompatible technologies and APIs in such a way that they can all work together seamlessly is huge. It’s like comparing a bottle rocket firecracker to a Saturn V launch vehicle.

Are you aware of the JSR223 add-on?

https://docs.openhab.org/configuration/jsr223.html

It lets you write rules in Jython, JavaScript, or Groovy.

Install and play with the REST API Documents (Misc tab in PaperUI Add-Ons).

Or look into MQTT. I use MQTT to publish sensor data from RPis to my OH.

That is an odd error. Show your calling Rule and relevant Item definitions. It looks like you are not passing an Item object for the fourth argument.

For the most robust code it should have been as Number for Temp.state as well. That was an oversite.

The problem is the Rules DSL is not very good at figuring out NumerItem states on its own. Probably what happened was using the as Number for the SetPoint have the Rules DSL enough to figure out how to cast the state of Temp to a Number for the comparison. But if both Temp and Setpoint lack the as Number then it can’t figure it out and the line generates errors.

Figuring out that is why I need to know how you are calling this lambda. It looks like you are trying to pass a plain old Number as one of the arguments to the lambda but you are using NumberItem and GenericItem for your types. Number is a completely different Type from NumberItem. They are not interchangable.

NumberItem is of type Item
NumberItem carries a State object which is of type DecimalType
DecimalType is of Type Number

So when I say “Temp.state as Number” I’m saying give me the State object from the NumberItem Temp, but I want to use it as a Number, not a DecimalType (or State, or Command).

I am running mosquitto on the Rpi to deal with sensor and button presses and I think Im going to leave it that way. It leaves me scope in the future to add other items either in the same way, or externally from the Rpi but with the same format.

At the minute, I dont think Ill start looking into the JSR223 you mention, just so there isnt another thing to add to the list.

So I get your points about the numbers/number items, and if there error is referring to the switch Item, then the question is, have I passed it into the function in the correct way?

Heres the items definitions.

//OAT hold off status
Switch OAT_Hi

//switches to store demands
Switch Heating_Demand
Switch HWS_Demand

and the temp sensors

Number sensor_1 "Living Room[%.1f'C]" {mqtt="<[mybroker:home/temperature/livingroom:state:default]"}
Number sensor_2 "Hot Water[%.1f'C]" {mqtt="<[mybroker:home/temperature/hotwater:state:default]"}
Number sensor_3 "Outside Air[%.1f'C]" {mqtt="<[mybroker:home/temperature/outsideair:state:default]"}
Number sensor_4 "Flow Pipe[%.1f'C]" {mqtt="<[mybroker:home/temperature/flowpipe:state:default]"}
Number sensor_5 "Upstairs[%.1f'C]" {mqtt="<[mybroker:home/temperature/upstairs:state:default]"}
Number sensor_6 "pin5[%.1f'C]" {mqtt="<[mybroker:home/test/value5:state:default]"}
Number sensor_7 "pin6[%.1f'C]" {mqtt="<[mybroker:home/test/value6:state:default]"}
Number sensor_8 "pin7[%.1f'C]" {mqtt="<[mybroker:home/test/value7:state:default]"}

and the setpoints items

Number Heating_Setpoint "Heating_Setpoint [%.1f'C]"
Number HWS_Setpoint "HWS_Setpoint [%.1f'C]"
Number OAT_HoldOff "OAT_HoldOff [%.1f'C]"

The whole rules file

import org.eclipse.xtext.xbase.lib.Functions

//determine if heating or HWS have any demand

//lambda to check temp, setpoint and demand

//Heating_Setpoint
//HWS_Setpoint
//OAT_HoldOff
//sensor_1 living room
//sensor_2 hot water
//sensor_3 OAT
//sensor_4 flow pipe
//sensor_5 upstairs temp

val Number Heating_Deadband = 1.0
val Number HWS_Deadband = 15.0

val Functions$Function4<NumberItem,NumberItem,NumberItem,GenericItem,Boolean> demand=[Temp,Setpoint,Deadband,Output|
                if(Temp.state < (Setpoint.state as Number - Deadband.state as Number)){
                        if(Output.state!=ON){
                                Output.sendCommand(ON)
                        }
                } else if(Temp.state >= Setpoint.state as Number){
                        if(Output.state!=OFF){
                                Output.sendCommand(OFF)
                        }
                }
        true
        ]

rule "OAT Hi check"
when
        System started or
        Item gOccupancyHeating received update
then
        demand.apply(sensor_3,OAT_HoldOff,1,OAT_Hi)

        if(OAT_HoldOff.state==ON){
                //low OAT
                demand.apply(sensor_1,Heating_Setpoint,Heating_Deadband,Heating_Demand)
        } else {
                Heating_Demand.sendCommand(OFF)
        }

        demand.apply(sensor_2,HWS_Setpoint,HWS_Deadband,HWS_Demand)


        //if(sensor_3.state < (OAT_HoldOff.state - 1){
        //      //low OAT temp
        //      if(OAT_Hi.state!=ON){
        //              OAT_Hi.sendCommand(ON)
        //      }
        //} else if(sensor_3.state > OAT_HoldOff.state){
        //      //high OAT temp
        //      if(OAT_Hi.state!=OFF){
        //              OAT_Hi.sendCommand(OFF)
        //      }
        //}


end

That is why I asked for how you are calling the lambda. Perhaps…

Side note on Item naming: Your Items model your home automation. It is always best, in the long run, to use meaningful names for Items. So, instead of “sensor_1” which doesn’t even tell you what kind of sensor it is, would be better named something like “Temperature_Livingroom”. Now you can look at the Item name and immediately know not only what it is but where it is.

You are passing the Number 1 but the Function is expecting a NumberItem in your third argument.

So you either need to change so you pass a NumberItem, or change the Function to expect a Number or Integer.

val Functions$Function4<NumberItem,NumberItem,Number,GenericItem,Boolean> demand=[

Another side note on naming: The typical convention is:

  • Items using the first letter capitalized or with a ‘g’ and second letter capitalized and using “_”
  • variables start with a lower case and use camelcase instead of “_”
  • constants are all caps

Using a naming convention like this (it doesn’t have to be this exactly but having some way to look at a name and know what it is) can save a lot of work (e.g. it would have saved me a couple of minutes trying to figure out what Headting_Deadband is).

So it looks like all you need to do is correct the type of the third argument to the Function.

Naming comvention isnt something ive put much thought into at the minute. Im just trying to get things going first then once I have a better idea of what the build will consist of I will have a better idea of how to arrange everything.

I changed NumberItem to Number and that error went away, but it keeps getting better :grinning:

2018-01-26 17:54:31.639 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Error during the execution of startup rule 'OAT Hi check': 'state' is not a member of 'java.lang.Integer'; line 20, column 47, length 14

which I believe is referring to this line of the function

val Functions$Function4<NumberItem,NumberItem,Number,GenericItem,Boolean> demand$
                if(Temp.state < (Setpoint.state as Number - Deadband.state as Nu$
                        if(Output.state!=ON){    <-------------this line
                                Output.sendCommand(ON)
                        }
                } else if(Temp.state >= Setpoint.state as Number){
                        if(Output.state!=OFF){
                                Output.sendCommand(OFF)
                        }
                }
        true
        ]

It looks like it is expecting an integer but Im passing a switch item. Is there a term for a switch item rather than GenericItem? I tried SwitchItem which didnt change the error. I assume that doesnt exist or its also wrong.

No, it is because Deadband is not a NumberItem, it has no .state.

Oh yeah. Ok, ive got rid of that error and that seems to be it now.

Thanks for your input here. I can see why you are the guru.

There a lot of info in this post alone that I can refer back to so hopefully I will have enough to keep me going for now. I dont think there is a lot more complex than this that I can see myself needing to do the work Im planning. Hopefully…

Thanks