Rule for changing temp and resume rule after power loss

thanks, they were both lower case, but I change them to Numberitem

Number Numberitem "Numberitem [%.1f °C]" <temperature>
rule "sets"

when   
    Item  Irrigation_Manual received command ON
then
    if(Irrigation_Zone_1.state == ON) {
        Numberitem.sendCommand(60)
    }
    if(Irrigation_Zone_2.state == ON) {
        Numberitem.sendCommand(70)
    }
    if(Irrigation_Zone_3.state == ON) {
        Numberitem.sendCommand(80)
    }
    if(Irrigation_Zone_4.state == ON) {
        Numberitem.sendCommand(86) 
 }
 else {
        /* Do Nothing */
    }
end

after turn on Irrigation_Manual the zone 1 automatically turn on and no set Numberitem comes as the picture of my log above shows.
I want use this number use in other rule for controlling heater.
what do you man about:trigger and method have to fit

Are you sure the rule gets triggered at all? Please use logInfo to ensure, the rule (and the conditional branch) are working as expected:

rule "sets"
when   
    Item  Irrigation_Manual received command ON
then
    logInfo("sets","Rule triggered")
    if(Irrigation_Zone_1.state == ON) {
        Numberitem.sendCommand(60)
        logInfo("sets","Zone_1 ON")
    }
    if(Irrigation_Zone_2.state == ON) {
        Numberitem.sendCommand(70)
        logInfo("sets","Zone_2 ON")
    }
    if(Irrigation_Zone_3.state == ON) {
        Numberitem.sendCommand(80)
        logInfo("sets","Zone_3 ON")
    }
    if(Irrigation_Zone_4.state == ON) {
        Numberitem.sendCommand(86) 
         logInfo("sets","Zone_4 ON")
}
 else {
        logInfo("sets","All Zones OFF")
        /* Do Nothing */
    }
end

This messages should appear in openhab.log

1 Like

This is a case where knowing what you are trying to accomplish can change the answer.

The first thing I’ll say is it sounds like you are trying to use OH as some sort of industrial control manager. It wasn’t designed for this and does not have the degree of reliability nor safety mechanism I would trust with an industrial process, especially one that is operating at such high temperatures.

If I assume from your statement that each “zone” will always be an hour then this cascading timers approach may not the best way to solve this problem.

To start:

  • Is my assumption correct and each time period is always 60 minutes?

  • Do the temperatures change based on some formula? For example, do they go up by a certain amount or a certain percentage until the halfway point and then ramp down in the same manner?

  • Do you ever have to cancel the process?

We cannot control when the Curr Items get restored so if your theory is correct, what needs to be done is to set a lockout flag that prevents any of the rules from running until they are all restored. But before we go down that path, answer the above questions because I don’t think this is the best approach any longer.

1 Like

Thanks to @rlkoshak and @Udo_Hartmann yes, we built a wood drier and wants to dry wood with these process.
60 minutes are correct but it follows several formula during the process, it follows 6 different formula during process.
yes cancel is an option for mistake in starting process.


last week we had to start the drier, so use other approach but it is uncompleted, I use arrays to reduce lines of code

Number tm "[%d]" <temperature> (All) { mqtt="<[mosquitto:openhab/tm:state:default]" }
Number goaltemp "[%d]" <temperature>
val int[] theArray = newArrayList(34, 35, 36, 37, 38, 39)

rule "run1"
when
    Item rsoozani_s received command ON or 
    Item tm received update
then
   goaltemp.sendCommand(theArray.get((tm.state as Number).intValue))
    
if(soozani_s.state == ON)  {
	
if((t.state as DecimalType)+1 > (goaltemp.state as DecimalType)) {
if(Heater.state != OFF) Heater.sendCommand(OFF)
}
else {
if(Heater.state != ON) Heater.sendCommand(ON)
}
	
}

so I have a goaltemp in item.
and i have tm that comes from esp8266, it sends numbers every 60 mins(first 60 mins= 1, second 60 mins=2 and so on)
with this code in esp8266
int tm=(millis()/3600000);
char* tmPayload = f2s(tm,0);
Serial.println™;
Serial.println(tmPayload);
client.publish(tmTopic, tmPayload);

so, when tm is 1, theArray.get get the first goal temp and set it. when tm is 2 …
millis start every time i press the process starts
that works…
now I want to do two things.
one delete esp8266 part and do this tm in rules. when a process being started, a millis begins and set tm a number every 60 mins, like what I did in esp8266
second is restore and resuming the process based on that tm number.

OK, I think a simplified version of the cascading timers approach may work but it might be simpler to use the Cancel Activity Design Pattern.

If I understand your problem (I had guessed you were running a wood drying kiln, drying lumber or blanks for turning?) I think something like the following will work. Note, again I’m just typing this in, it likely has errors.

Preconditions:

  • make sure you have persistence set up with restoreOnStartup

Items:

Number GoalTemp "Goal Temp [%d]" <temperature>
Number ProcessStep "Process Step [%d]"
Switch ProcessTimer { expire="60m,command=OFF" }
String ControlProcess "Control drying process"

Rules:

val targetTemps = newArrayList(34, 35, 36, 37, 38, 39, ...) // enter the target temps for each step of the process

rule "Control the process"
when
    Item ControlProcess received command
then
    switch receivedCommand.toString {

        case "Start": ProcessStep.sendCommand(0)

        case "Pause": {
            Heater.sendCommand(OFF) // turn off the heater
            ProcessTimer.postUpdate(OFF) // cancel the timer
        }

        // Note: Resume will run the current step the full 60 minutes regardless of how much time in the step was run before the pause
        case "Resume":  ProcessStep.sendCommand(ProcessStep.state) // resume the process by sending the current state again

        case "Stop": ProcessStep.sendCommand(-1) // stop the process by sending the end state
    }
end

rule "A step has completed"
when
    Item ProcessTimer received command OFF
then
    val currStep = ProcessStep.state as Number
    val nextStep = currStep + 1

    // if nextStep is the size of the array of target temps we have finished the last step
    if(nextStep == targetTemps.size) ProcessStep.sendCommand(-1)
    else ProcessStep.sendCommand(nextStep)
end

rule "Transition to new step"
when
    Item ProcessStep received command
then
    val currStep = receivedCommand as Number

    // Turn off the heater if we were cancelled, reach the last step, or the curr step is greater than the total number of steps
    if(currStep == -1 || currStep >= numSteps) {
        Heater.sendCommand(OFF)
        ProcessTimer.postUpdate(OFF)
    }

    // Transition to the next step, set the GoalTemp from the array, turn on the heater if it isn't already, and start the timer
    else {
        GoalTemp.sendCommand(targetTemps.get(currStep))
        if(Heater.state != ON) Heater.sendCommand(ON)
        ProcessTimer.sendCommand(ON)
    }
end

rule "Resume the process if it was running when OH went down"
when
    System started
then
    Thread:sleep(1000) // give everything time to be restored from persistence

    // If the Timer was on the process was running, restart the current step
    if(ProcessTimer.state == ON)  ProcessStep.sendCommand(ProcessStep.state)

    // If the process wasn't running, make sure the Heater is OFF
    else Heater.sendCommand(OFF)
end

Sitemap:

    Switch item=ControlProcess mappings=[Start=Start,Pause=Pause,Resume=Resume,Stop=Stop]

This will put a line on your sitemap with four buttons labeled Start, Pause, Resume, and Stop. You can get clever using the visibility Flag to swap out the Pause and Resume buttons based on whether the process is running or not.

While this approach resuses some of the same concepts as the cascading timers, I think it has become simplified enough now to avoid the problems you experienced last time.

You will probably want to add a bunch of log statements to watch it go through the steps while you test it.

I hope it works for you!

1 Like

tnx :slightly_smiling_face:
yes it is a wood drying kiln.
just change

ProcessStep.sendCommand(ProcessStep.state)

to

ProcessStep.postUpdate(ProcessStep.state)

it has error and now fixed.
I have only one error left

GoalTemp.sendCommand(targetTemps.get(nextStep))

the nextStep part has error:
The method or field nextStep is undefined

edit:
change this part of code and add val nextStep and change get array part and it seems problem solves! im going to test it :slightly_smiling_face:

rule "Transition to new step"
when
    Item ProcessStep received command
then
    val currStep = receivedCommand as Number
    val nextStep = (currStep + 1) 

    // Turn off the heater if we were cancelled, reach the last step, or the temperatures step is greater than the total number of steps
    if(currStep == -1 || currStep >= 7) {
        Heater.sendCommand(OFF)
        ProcessTimer.postUpdate(OFF)
    }

    // Transition to the next step, set the GoalTemp from the array, turn on the heater if it isn't already, and start the timer
    else {
        GoalTemp.sendCommand(targetTemps.get((nextStep).intValue))
       // if(Heater.state != ON) Heater.sendCommand(ON)
        ProcessTimer.sendCommand(ON)
        if((t.state as DecimalType)+2 > (GoalTemp.state as DecimalType)) {
        if(Heater.state != OFF) Heater.sendCommand(OFF)
    }
       else {
       if(Heater.state != ON) Heater.sendCommand(ON)
    }
}
end

I test it, for testing change 60m to 60s …after a min processtimer does not turn off…
is it because of what I did with nextStep cause the problem? now I have two val nextStep!
tnx

You have to use sendCommand on ProcessStep or else the rule “Transition to new step” won’t trigger. What was the error?

1 Like

tnx

ProcessStep.sendCommand(ProcessStep.state)

error:
Type mismatch: cannot convert from State to String

GoalTemp.sendCommand(targetTemps.get(nextStep))

error:
The method or field nextStep is undefined

@rlkoshak
change the ProcessStep.state to

ProcessStep.sendCommand(ProcessStep.state.toString)

solve this error, is this Ok?
nextStep warning is still on the table

That is what I was going to suggest to fix the String error.

I need to get to a computer to look at the other error. I wonder if it doesn’t like that nextStep isn’t explicitly an int.

Try using nextStep.intValue

Sometimes the rules engine complains about things not existing when the real problem is it can’t cover it to the right type.

1 Like

Doh! Change the line to:

GoalTemp.sendCommand(targetTemps.get(currStep)

That was a silly mistake on my part.

1 Like