How to implement timer with parameter

I am daring to revive this thread for various reasons:

  1. I am trying to implement this sprinkler / irrigation approach
  2. Silent changes were made during the discussion
  3. I am also having trouble making this work
  4. I though there is value in it for those who want to work with the thread’s solution.

I have been abstinent form OH v1 for quite a while, and seem to have forgotten a few things.
I basically implemented the following based on this thread:

sitemap irrigation label="Irrigation" {
  Frame {
    Group item=gIrriAll		label="Irrgiation Test"		icon="water_drops"
    Setpoint item=Irrigation1_1_Time|minValue=1|maxValue=30|step=1|
    Setpoint item=Irrigation1_2_Time|minValue=1|maxValue=30|step=1|
    Setpoint item=Irrigation1_3_Time|minValue=1|maxValue=30|step=1|
  }
}
Group   gIrriAll
Group   gIrrigation													(gIrriAll)
Group   gIrrigationTime												(gIrriAll)
Switch  Irrigation_1				"Zone 1 valve"					(gIrrigation, gIrriAll)
Switch  Irrigation_2				"Zone 2 valve"					(gIrrigation, gIrriAll)
Switch  Irrigation_3				"Zone 3 valve"					(gIrrigation, gIrriAll)
String  Irrigation_Curr				"Current active zone is [%s]"	(gIrriAll)
Switch  Irrigation_Auto				"Irrigation Auto"				(gIrriAll)
Switch  Irrigation_Now				"Irrigation Now"				(gIrriAll)
Number  Irrigation_1_Time 			"Zone 1 duration [%d mins]"		(gIrrigationTime, gIrriAll)
Number  Irrigation_2_Time 			"Zone 2 duration [%d mins]"		(gIrrigationTime, gIrriAll)
Number  Irrigation_3_Time 			"Zone 3 duration [%d mins]"		(gIrrigationTime, gIrriAll)
Switch  Irrigation_Allowed			"Test switch to trigger cascade"	(gIrriAll)
import org.openhab.core.library.types.*
import org.openhab.core.library.items.*
import org.openhab.core.types.*
import org.openhab.core.items.*
import org.openhab.core.persistence.*
import org.openhab.model.script.actions.*
import org.eclipse.xtext.xbase.lib.*
import org.joda.time.*
import org.openhab.library.tel.types.CallType
import java.util.Set

// ------------------------------------------------------------------------

val Timer  irrigationTimer   = null

// https://community.openhab.org/t/how-to-implement-timer-with-parameter/10600/7

// If you have persistence and it does not save Strings use this rule
rule "System started"
  when
    System started
  then
    logInfo("Irrigation.0", "System Start: Irrigation .")
    // assumes no more than one is ON at a time
    val curr = gIrrigation.members.filter[v|v.state == ON].head
    logInfo("Irrigation.0", "System Start: Irrigation ..")
    
    if(curr == null) {
      Irrigation_Curr.sendCommand("None")
      logInfo("Irrigation.0", "System Start: Irrigation ...")
    } else {
      Irrigation_Curr.sendCommand(curr.name)
      logInfo("Irrigation.0", "System Start: Irrigation ....")

      // retrigger the cascade rule to recreate the timer
      curr.sendCommand(ON)
      logInfo("Irrigation.0", "System Start: Irrigation .....")
    }
    
    // any newly created timer duration variable won't be set until intitialised
    if (Irrigation_1_Time.state == Uninitialized) {
      logInfo("Irrigation.0", "System Start: Irrigation initialise state: Irrigation_1_Time")
      Irrigation_1_Time.postUpdate(5)
    }
    if (Irrigation_2_Time.state == Uninitialized) {
      Irrigation_2_Time.postUpdate(5)
      logInfo("Irrigation.0", "System Start: Irrigation initialise state: Irrigation_2_Time")
    }
    if (Irrigation_3_Time.state == Uninitialized) {
      Irrigation_3_Time.postUpdate(5)
      logInfo("Irrigation.0", "System Start: Irrigation initialise state: Irrigation_3_Time")
    }
    logInfo("Irrigation1.0", "System Start: Irrigation initialise state: done.")
    
    // does not take into account how long it has already been running
end


rule "Start Irrigation"
  when
    Item Irrigation_Allowed changed to ON
  then
    if(Irrigation_Auto.state == ON || Irrigation_Now.state == ON){
      // start with valve 1
      logInfo("Irrigation.0", "Irrigation started...")
      Irrigation_Curr.sendCommand(Irrigation_1.name)
      Irrigation_1.sendCommand(ON)
      logInfo("Irrigation.1", "... turning on {}", Irrigation_Curr.state)
    }
end

rule "Irrigation switch cascade"
  when
    Item Irrigation_1 received command ON or
    Item Irrigation_2 received command ON or
    Item Irrigation_3 received command ON
  then
    // Get the currently running valve
    val currValveName   = Irrigation_Curr.state.toString
    //logInfo("Irrigation.2", "... currValveName..: {}", currValveName)
    
    val currValveSwitch = gIrrigation.members.filter[i|i.name == currValveName].head
    //logInfo("Irrigation.3", "... currValveSwitch: {}", currValveSwitch.state)
    
    val currValveNum    = Integer::parseInt(currValveName.split("_").get(1))
    //logInfo("Irrigation.4", "... currValveNum...: {}", currValveNum)
    
    val currValveMins   = gIrrigationTime.members.filter[t|t.name == currValveName + "_Time"].head.state as DecimalType
    //logInfo("Irrigation.5", "... currValveMins..: {}", currValveMins)
    
    // Get the next running valve in the sequence
    val nextValveNum    = currValveNum + 1
    val nextValveName   = "Irrigation_" + nextValveNum
    
    // will be null if not found
    val nextValveSwitch = gIrrigation.members.filter[i|i.name == nextValveName].head

    // Create a timer to turn off curr and turn on next
    irrigationTimer = createTimer(now.plusMinutes(currValveMins.intValue), [|
    logInfo("Irrigation", "Turing off " + currValveName)
    currValveSwitch.sendCommand(OFF)

    // without the sleep commands OFF one ON the next follow too quickly
    Thread::sleep(2000)

    if(nextValveSwitch != null) {
      logInfo("Irrigation", "Turing on " + nextValve.name)
      Irrigation_Curr.sendCommand(nextValveName)
      nextValveSwitch.sendCommand(ON)
    } else {
      logInfo("Irrigation", "Irrigation is complete")
      Irrigation_Curr.sendCommand("None")
    }
    irrigationTimer = null
    ])
end

rule "Cancel Irrigation"
  when
    Item Irrigation_Allowed received command OFF
  then
    // Cancel any timers
    if(irrigationTimer != null && !irrigationTimer.hasTerminated) {
      irrigationTimer.cancel
    }
    irrigationTimer = null
    
    // Turn off any open valves
    gIrrigation.members.forEach[v | if(v.state != OFF) v.sendCommand(OFF) ]
    
    // Update the status
    Irrigation_Curr.sendCommand("None")
    logInfo("Irrigation", "Irrigation stopped / ended / finished.")
end

The silent change that was made (Rich’s initial line)

    // Get the next running valve in the sequence
    val nextValveName   = "Irrigation_" + currValveNum

should read (as reflected in my listing)

    // Get the next running valve in the sequence
    val nextValveName   = "Irrigation_" + nextValveNum

Persistence in rrd4j.persist:

  gIrriAll			: strategy = everyChange, restoreOnStartup

Errors I encounter…

In start-up

[ERROR] [m.r.internal.engine.RuleEngine] - Error during the execution of startup rule 'System started': cannot invoke method public abstract java.lang.String org.openhab.core.items.Item.getName() on null

which is related to this line:

    Irrigation_Curr.sendCommand(curr.name)

The next error is:

[ERROR] [o.o.c.s.ScriptExecutionThread ] - Error during the execution of rule 'Irrigation switch cascade': Cannot cast org.openhab.core.types.UnDefType to org.openhab.core.library.types.DecimalType

2

related to this line:

    val currValveMins   = gIrrigationTime.members.filter[t|t.name == currValveName + "_Time"].head.state as DecimalType

Any hints appreciated.

[2018-03-10 Updated] incorporated all changes to make this a working solution