Can Lambda Function Access external data that is not an item

  • Platform information:
    • Hardware: NANOPI NEO 2 Plis/1GB Ram/16 Storage
    • OS: Armbian with Kernel 5.15
    • openHAB version:3.3 or 4.0
      I want a create a function that is easy replicable to create Random answer to avoid to get always the same answer…
      So i wrote this code:
var String WC_POO_SEQ="XXXXXXXXXXXXXXXX"
var WC_POO_CUR=0
val Functions$Function0 < String> WC_POO_ANSWER = [ R_SECONDS |
	var Answer = ""

	if (WC_POO_CUR==0){											//Cambia Qui						
								WC_POO_CUR=8					//Cambia Qui	
								WC_POO_SEQ="01234567m"			//Cambia qui devono esserci almeno numero della riga precedente-1 caratteri.
								}
		val java.util.Random rand = new java.util.Random
		var randy = Math.abs(rand.nextInt())
		randy =(randy % WC_POO_CUR.intValue)
		val Frase = WC_POO_SEQ.charAt(randy).toString

		switch(Frase) {
			case '0': {	Answer ='<speak><amazon:emotion name="disappointed" intensity="high">Eri Bellissimo...</amazon:emotion></speak>'}
			case '1': {	Answer ='<speak><amazon:emotion name="disappointed" intensity="high">Quando ti ho detto che per apprezzare una persona dovevi guardare la sua bellezza interiore... Non mi riferivo al frigorifero!</amazon:emotion></speak>'}
			case '2': {	Answer ='<speak><amazon:emotion name="disappointed" intensity="high">Hei Tu porco, levàle le mani di dosso!</amazon:emotion></speak>'}
			case '3': {	Answer ='<speak><amazon:emotion name="disappointed" intensity="high">Non ci provare!</amazon:emotion></speak>'}			
			case '4': {	Answer ='<speak><amazon:emotion name="disappointed" intensity="high">Chiudi subito quella porta!</amazon:emotion></speak>'}
			case '5': {	Answer ='<speak><amazon:emotion name="disappointed" intensity="medium">Ti ricordo che sei a dieta!</amazon:emotion></speak>'}						
			case '6': { Answer ='<speak><amazon:emotion name="disappointed" intensity="medium">Che cavolo stai facendo Willis?</amazon:emotion></speak>'}						
			case '7': { Answer ='<speak>Come al solito! Si chiude una porta... e si apre il frigorifero!</speak>'}
			case '8': {Answer ='error'}
	} /* End of switch */
	WC_POO_SEQ = WC_POO_SEQ.replace(Frase, '')
	WC_POO_CUR=WC_POO_CUR-1
	logInfo("ALEXA", "Cur: {} Seq: {} Risposta: {}",WC_POO_CUR, WC_POO_SEQ, Answer )	
	return Answer; 
]

The idea was replicate this lamba funtion for every answer that in need.
The funtion in this case give 8 random answer without repeat the answer until he has not said all 8 answers…
Unfortunately i need 2 external variables (maybe i can decrease to one) to store how many sentences are already said and wich ones…
Unfortunately i spent one evening to understand that lambafunction cannot read external variables…
I try to pass these variables as parameters… but i can only read, not modifiy…
I dont want ho use an ITEM because i have already many items and also everytime i have to define another routine to answer i have to modify the rules files and also the items file…
Any “clean” solution?
At the moment the only solution that i see are external python files bu i suppose that need more resources…

rule "Apertura Frigorifero"
	when
        	Item PORTA_FRIGO_STATUS changed to OFF
	then
            Reply = executeCommandLine(Duration.ofSeconds(20), "/etc/openhab/python/refrigerator.py")
            Do something with Reply
end

Thanks in advance
Enrico

You might look into the new ChatGPT add-on to meet this requirement. This is exactly the use case that you are after.

Should be able to modify. how did you define them and how did you pass them?

Why? Using one Item shouldn’t require a modification to the .items file, just the .rules file, given this current approach.

If you stick with this overall approach then you could use the sharedCache to store your variables. Any rule can access anything in the sharedCache. See the Implicit Variables section of the Rules docs.

You can make the code simpler and your job easier if you choose at random and don’t care if there are repeats.

1 Like

Rich you answer to all my stupid questions! If you visit Italy i have to offer a beer!

At the moment i solved with an external executable made from c. Reaction time of the rule calling external C is 35ms. Rule only is ~25ms. Extrenal pyton was too slow ~300ms…
You re right, once created the item… i don’t have to modify the item files… but to create the new “procedure” yes… and considering that often i’m modifying rules while i’m on the bus from mobile i like more an approach without adding more items… i’t works and i’m happy… but this is not my work i’m here to learn and have fun… so try to find all possible solutions is the best way to learn.

This will be the solution perfect for me and i will learn more about lambda functions!

var String WC_POO_SEQ="XXXXXXXXXXXXXXXX"
var Number WC_POO_CUR=0
val Functions$Function2 <String, Number, void> WC_POO_ANSWER = [SEQ ,CUR |
CUR=CUR+1
	var Answer = CUR.toString
logInfo("Cacca Frase","JavaM1")
//	if (WC_POO_CUR==0){											
//								WC_POO_CUR=8					//Cambia Qui Metti il numero delle frasi. Visto che i case partono da 0 se l'ultimo case e' 7 le frasi sono 8! 	
logInfo("Cacca Frase","JavaM1.5")
//								WC_POO_SEQ="01234567m"			//Cambia qui devono esserci almeno numero della riga precedente-1 caratteri.
//								}
logInfo("Cacca Frase","JavaM2")
//		val java.util.Random rand = new java.util.Random
//		var randy = Math.abs(rand.nextInt())
//		randy =(randy % WC_POO_CUR.intValue)
//		val Frase = WC_POO_SEQ.charAt(randy).toString
//
//		switch(Frase) {
//			case '0': {	Answer ='Frase "1"'}
//			case '1': {	Answer ='Frase 2'}
//			case '2': {	Answer ='Frase 3'}
//			case '3': {	Answer ='Frase 4'}			
//			case '4': {	Answer ='Frase 5'}
//			case '5': {	Answer ='Frase 6'}						
//			case '6': { Answer ='Frase 7'}						
//			case '7': { Answer ='Frase 8'}
//			case '8': {Answer ='error'}
//	} /* End of switch */
//	WC_POO_SEQ = WC_POO_SEQ.replace(Frase, '')
//	WC_POO_CUR=WC_POO_CUR-1
//	logInfo("ALEXA", "Cur: {} Seq: {} Risposta: {}",WC_POO_CUR, WC_POO_SEQ, Answer )	
	return Answer; 
]

When the function is launched i got an error on cur=cur+1
here is where i call the lambda:

var String Answ="poi"
Answ=WC_POO_ANSWER.apply(WC_POO_SEQ,WC_POO_CUR)

and yes name of variables are very weird…
Are rules about WC. (if i forgot open the cat enter and try to drink) so i 3d printed a sensor…
And after long work i decided to make alexa speak when i open or close the wc.
There is a trigger for opening the first layer of the wc (i have no idea how people say in egnlish) a trigger when i fully open the wc, and when i clean the cat wc just few minutes before the Alexa answers are different…
So my goal is simply move away the switch with all possible answer to have a rule more readable.
Regards.
Enrico.

You should look into Blockly rules. Much easier to manage on a phone since it’s graphical.

It’s better to define it like this:

val WC_POO_ANSWER = [ String SEQ, Number CUR |

This might be a tricky one. Because WC_POO_CUR is a simple Number it might be passed by value instead of by reference. Why this matters is that you are adding 1 to a copy of the variable instead of the actual variable. It still shouldn’t generate an error though. What’s the error?

I wonder if the error has nothing to do with the CURR = CURR + 1.

When you defined the lambda with <String, Number, void> you told it that it takes two arguments, the first is a String and second is a Number. And then you told it that the function returns a void but your code is actually returning a String.

I wrote a simple function and a simple rule just to check:

var String WC_POO_SEQ="XXXXXXXXXXXXXXXX"
var Number WC_POO_CUR=0
val WC_POO_TEST = [ String SEQ, Number CUR |
CUR=CUR+1
logInfo("Frase",CUR.toString)
]

rule "120"

when
  Item CAT_WC_BUSY changed to ON
then
WC_POO_TEST.apply(WC_POO_SEQ,WC_POO_CUR)
end

I like more this sintax and now it warn me!!!

2023-06-11 12:01:23.421 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'deleteme.rules'
2023-06-11 12:01:24.914 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'deleteme.rules', using it anyway:
Assignment to final parameter

if i run the rule:

2023-06-11 12:01:49.993 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'deleteme-1' failed: Couldn't invoke 'assignValueTo' for feature param CUR in deleteme

Of course is not possible to pass CUR with & like in other languages…
I read that is possible to do with an array or a MAP variable but dont understood yet how MAP works…

Bingo!!!
With List, i was able to pass multiple

	val List<Integer> theArray = newArrayList(1, 2, 3, 4, 5, 6, 7, 8)    
    val miaLambda = [ List<Integer> lista |
        for (Object elemento : lista) {
            logInfo("Regola DSL", "Elemento nella lista: " + elemento.toString)
        }
	lista.set(2, lista.get(2)+1)

    ]

rule "119"
when
  Item CAT_WC_BUSY changed to ON
then

   miaLambda.apply(theArray)

        for (Object elemento : theArray) {
            logInfo("Regola DSL", "Elemento nella lista: " + elemento.toString)
        }

end

Again, why not use the sharedCache? It’s basically a Map that serves as a place to store variables that can be shared across rules.

1 Like