[SOLVED] Thermostat Rule Help (gpio, dht22, exec binding, things, items, rules)

Hello everyone!

So I’ve been using, or should I say learning, OH2 for a couple of months now… WOW! The software is great and the community is full of talented people willing to help beginners like me. So thank you to those people… you know who you are!

I’m working on a thermostat project, so I wanted to share for others to use and to get some feedback / advice on how to improve. Any constructive criticism or advice is welcomed! I’m going to add some additional functionality, which I’ll share with the OH community as it progresses.

Note: The setup is working as expected, but there are a lot of logs being generated on behalf of the rule I wrote.

Thanks in advance for any help provided!!!

Setup…

Openhabian running on RaspPi (Linux openHABianPi 4.14.34-v7+ #1110 SMP Mon Apr 16 15:18:51 BST 2018 armv7l GNU/Linu)
DHT22 hooked to gpis (Values read with python script executed via exec binding… man it was tough figuring it out!! Rule to change string into number)
16 channel relay board to switch heating, cooling, and fan.

hvac.items

//-----Proxy items used for logic-----//
Switch AC "AC" (HVAC) 
Switch Heat "Heat" (HVAC) 
Switch Fan "Fan" (HVAC) 

//-----Number used for home.sitemap Setpoint-----//
Number Setpoint_Temp "HVAC Setpoint [%.1f °F]" <temperature> (HVAC)

//-----Living_Temp and Living_Hum numbers converted from the strings below (Note: See ../things/hvac.things)-----//
Number Living_Hum_Numb "Living Humidity [%.1f %%]" <humidity> (HVAC)
Number Living_Temp_Numb "Living Temp [%.1f °F]" <temperature> (HVAC)

//-----Strings read from DHT22 temp/humidity sensor-----//
String Living_Hum {channel="exec:command:hum:output"}
String Living_Temp {channel="exec:command:temp:output"}

//-----Relay Items used to turn on/off air handler-----//
Switch  AC_Relay   {gpio="pin:21 activelow:yes initialValue:high"}
Switch  Heat_Relay {gpio="pin:20 activelow:yes initialValue:high"}
Switch  Fan_Relay  {gpio="pin:16 activelow:yes initialValue:high"}

hvac.things

Thing exec:command:temp [command="/usr/bin/python2  /etc/openhab2/scripts/temp.py", interval=30, autorun=true]
Thing exec:command:hum [command="/usr/bin/python2  /etc/openhab2/scripts/hum.py", interval=300, autorun=true]

temp.py and hum.py

#!/usr/bin/env python2.7

import sys

import Adafruit_DHT

sensor = Adafruit_DHT.DHT22

pin = 4

humidity, temperature = Adafruit_DHT.read_retry(sensor, pin)

temperature = temperature * 9/5.0 + 32

if humidity is not None and temperature is not None:

    print('{0:0.2f}'.format(temperature))

else:

    print('Failed to get reading. Try again!')

sys.exit(1)
---------------------------------------------------------------------------------------
#!/usr/bin/env python2.7
import sys

import Adafruit_DHT

sensor = Adafruit_DHT.DHT22

pin = 4

humidity, temperature = Adafruit_DHT.read_retry(sensor, pin)

if humidity is not None and temperature is not None:

     print('{0:0.2f}'.format(humidity))

else:

     print('Failed to get reading. Try again!')

sys.exit(1)


hvac.rules

//-----Rule to convert String type to Number type-----//
rule "Convert dht22 temp string to number"
  when
        Item Living_Temp changed
  then
        Living_Temp_Numb.postUpdate(Float::parseFloat(String::format("%s",Living_Temp.state).replace(' ','')))
end


rule "Convert dht22 humidity to number"
  when
        Item Living_Hum changed
  then
        Living_Hum_Numb.postUpdate(Float::parseFloat(String::format("%s",Living_Hum.state).replace(' ','')))
end

//-----Rule to postUpdate of Living_Temp_Numb every minute-----//
//-----Used to turn off AC if Setpoint changed but temp hasn't changed-----//
rule "Update Living Temp Number every minute"
  when 
        Time cron "0 0/1 * 1/1 * ? *"
  then
        sendCommand(Living_Temp_Numb.name,Living_Temp_Numb.state.toString)
end

//-----Rule to turn off/on AC (Auto)-----//
rule "Turn AC on if AC switch is ON and Living Temp is greater than Setpoint"
  when 
        Item Living_Temp_Numb received command or
        Item Living_Temp_Numb changed 
  then 
        if (AC.state==ON && Living_Temp_Numb.state > Setpoint_Temp.state)
    {
        AC_Relay.sendCommand(ON)
        Thread::sleep(5000)
        Fan.sendCommand(ON)
        Fan_Relay.sendCommand(ON)
    }   
        else
    {
        AC_Relay.sendCommand(OFF)
        Thread::sleep(5000)
        Fan.sendCommand(OFF)
        Fan_Relay.sendCommand(OFF)
    }
        
  end

rule "Turn AC off if AC switch changes from ON to OFF"
  when 
        Item AC changed from ON to OFF
  then 
        Thread::sleep(2000)
        AC_Relay.sendCommand(OFF)
        Thread::sleep(5000)
        Fan_Relay.sendCommand(OFF)
        Fan.sendCommand(OFF)
  end

events.logs (50 lines)

2018-07-05 07:35:05.095 [ome.event.ItemCommandEvent] - Item 'Fan_Relay' received command ON
2018-07-05 07:36:00.023 [ome.event.ItemCommandEvent] - Item 'Living_Temp_Numb' received command 78.08
2018-07-05 07:36:00.089 [ome.event.ItemCommandEvent] - Item 'AC_Relay' received command ON
2018-07-05 07:36:05.107 [ome.event.ItemCommandEvent] - Item 'Fan' received command ON
2018-07-05 07:36:05.122 [ome.event.ItemCommandEvent] - Item 'Fan_Relay' received command ON
2018-07-05 07:37:00.027 [ome.event.ItemCommandEvent] - Item 'Living_Temp_Numb' received command 78.08
2018-07-05 07:37:00.063 [ome.event.ItemCommandEvent] - Item 'AC_Relay' received command ON
2018-07-05 07:37:05.068 [ome.event.ItemCommandEvent] - Item 'Fan' received command ON
2018-07-05 07:37:05.074 [ome.event.ItemCommandEvent] - Item 'Fan_Relay' received command ON
2018-07-05 07:37:37.999 [vent.ItemStateChangedEvent] - ZWaveNode2ZW100Multisensor6_MotionAlarm changed from OFF to ON
2018-07-05 07:38:00.021 [ome.event.ItemCommandEvent] - Item 'Living_Temp_Numb' received command 78.08
2018-07-05 07:38:00.053 [ome.event.ItemCommandEvent] - Item 'AC_Relay' received command ON
2018-07-05 07:38:05.073 [ome.event.ItemCommandEvent] - Item 'Fan' received command ON
2018-07-05 07:38:05.086 [ome.event.ItemCommandEvent] - Item 'Fan_Relay' received command ON
2018-07-05 07:38:15.521 [vent.ItemStateChangedEvent] - ZWaveNode2ZW100Multisensor6_MotionAlarm changed from ON to OFF
2018-07-05 07:39:00.024 [ome.event.ItemCommandEvent] - Item 'Living_Temp_Numb' received command 78.08
2018-07-05 07:39:00.072 [ome.event.ItemCommandEvent] - Item 'AC_Relay' received command ON
2018-07-05 07:39:05.084 [ome.event.ItemCommandEvent] - Item 'Fan' received command ON
2018-07-05 07:39:05.101 [ome.event.ItemCommandEvent] - Item 'Fan_Relay' received command ON
2018-07-05 07:39:41.657 [vent.ItemStateChangedEvent] - Living_Hum changed from 53.10 to 53.30
2018-07-05 07:39:41.670 [vent.ItemStateChangedEvent] - Living_Hum_Numb changed from 53.1 to 53.3
2018-07-05 07:40:00.032 [ome.event.ItemCommandEvent] - Item 'Living_Temp_Numb' received command 78.08
2018-07-05 07:40:00.061 [ome.event.ItemCommandEvent] - Item 'AC_Relay' received command ON
2018-07-05 07:40:05.068 [ome.event.ItemCommandEvent] - Item 'Fan' received command ON
2018-07-05 07:40:05.087 [ome.event.ItemCommandEvent] - Item 'Fan_Relay' received command ON
2018-07-05 07:40:13.235 [vent.ItemStateChangedEvent] - ZWaveNode2ZW100Multisensor6_MotionAlarm changed from OFF to ON
2018-07-05 07:40:52.477 [vent.ItemStateChangedEvent] - ZWaveNode2ZW100Multisensor6_MotionAlarm changed from ON to OFF
2018-07-05 07:41:00.032 [ome.event.ItemCommandEvent] - Item 'Living_Temp_Numb' received command 78.08
2018-07-05 07:41:00.069 [ome.event.ItemCommandEvent] - Item 'AC_Relay' received command ON
2018-07-05 07:41:05.087 [ome.event.ItemCommandEvent] - Item 'Fan' received command ON
2018-07-05 07:41:05.099 [ome.event.ItemCommandEvent] - Item 'Fan_Relay' received command ON
2018-07-05 07:42:00.027 [ome.event.ItemCommandEvent] - Item 'Living_Temp_Numb' received command 78.08
2018-07-05 07:42:00.125 [ome.event.ItemCommandEvent] - Item 'AC_Relay' received command ON
2018-07-05 07:42:05.151 [ome.event.ItemCommandEvent] - Item 'Fan' received command ON
2018-07-05 07:42:05.168 [ome.event.ItemCommandEvent] - Item 'Fan_Relay' received command ON
2018-07-05 07:43:00.080 [ome.event.ItemCommandEvent] - Item 'Living_Temp_Numb' received command 78.08
2018-07-05 07:43:00.114 [ome.event.ItemCommandEvent] - Item 'AC_Relay' received command ON
2018-07-05 07:43:03.877 [vent.ItemStateChangedEvent] - Living_Temp changed from 78.08 to 78.26
2018-07-05 07:43:03.898 [vent.ItemStateChangedEvent] - Living_Temp_Numb changed from 78.08 to 78.26
2018-07-05 07:43:03.913 [ome.event.ItemCommandEvent] - Item 'AC_Relay' received command ON
2018-07-05 07:43:05.123 [ome.event.ItemCommandEvent] - Item 'Fan' received command ON
2018-07-05 07:43:05.133 [ome.event.ItemCommandEvent] - Item 'Fan_Relay' received command ON
2018-07-05 07:43:08.924 [ome.event.ItemCommandEvent] - Item 'Fan' received command ON
2018-07-05 07:43:08.932 [ome.event.ItemCommandEvent] - Item 'Fan_Relay' received command ON
2018-07-05 07:44:00.046 [ome.event.ItemCommandEvent] - Item 'Living_Temp_Numb' received command 78.26
2018-07-05 07:44:00.090 [ome.event.ItemCommandEvent] - Item 'AC_Relay' received command ON
2018-07-05 07:44:05.110 [ome.event.ItemCommandEvent] - Item 'Fan' received command ON
2018-07-05 07:44:05.124 [ome.event.ItemCommandEvent] - Item 'Fan_Relay' received command ON
2018-07-05 07:44:43.258 [vent.ItemStateChangedEvent] - Living_Hum changed from 53.30 to 53.00
2018-07-05 07:44:43.273 [vent.ItemStateChangedEvent] - Living_Hum_Numb changed from 53.3 to 53.0```

To reduce the log-entries you can change

rule "Turn AC on if AC switch is ON and Living Temp is greater than Setpoint"
  when 
//        Item Living_Temp_Numb received command or
        Item Living_Temp_Numb changed 
  then 
        if (AC.state==ON && Living_Temp_Numb.state > Setpoint_Temp.state)
    {
//If (AC_Relay.state != ON || Fan.state != ON || Fan-Relay.state != ON) {
If (AC_Relay.state != ON) {
        AC_Relay.sendCommand(ON)
        Thread::sleep(5000)
        Fan.sendCommand(ON)
        Fan_Relay.sendCommand(ON)
    }}   
        else
    {
If (AC_Relay.state != OFF) {
        AC_Relay.sendCommand(OFF)
        Thread::sleep(5000)
        Fan.sendCommand(OFF)
        Fan_Relay.sendCommand(OFF)
    }}
   end

You can replace .sendCommand() with .postUpdate().

Thanks for the suggestion hr3! Just to make sure we’re on the same page…

the rule “Turn AC on if AC switch is ON and Living Temp is greater than Setpoint” should look like this?

rule "Turn AC on if AC switch is ON and Living Temp is greater than Setpoint"
  when
       Item Living_Temp_Numb changed
  then
       if (AC.state==ON && Living_Temp_Numb.state > Setpoint_Temp.state) 
          {
             if (AC.state != ON || Fan.state != ON || Fan_Relay.state != ON)  
              {
             if (AC_Relay.state != ON)  
              {
            AC_Relay.sendCommand(ON)
            Thread::sleep(5000)
            Fan.sendCommand(ON)
            Fan_Relay.sendCommand(ON)
             }}
            else
             {
            if (AC_Relay.state != OFF)
             {
            AC.Relay.SendCommand(OFF)
            Thread::sleep(5000)
            Fan.sendCommand(OFF)
            Fan_Relay.sendCommand(OFF)
             }}

And I need to use postUpdate here?

rule “Update Living Temp Number every minute”
when
Time cron “0 0/1 * 1/1 * ? *”
then
sendCommand(Living_Temp_Numb.name,Living_Temp_Numb.state.toString)
end

Thanks for the help Harry.

Not a good idea to use Thread::sleep for more than a second or 2
Use a timer
And add a test is the AC_Relay is already on or off to avoid duplicating sending command every minute

var Timer timer = null

//-----Rule to postUpdate of Living_Temp_Numb every minute-----//
//-----Used to turn off AC if Setpoint changed but temp hasn't changed-----//
rule "Update Living Temp Number every minute"
  when 
        Time cron "0 0/1 * * * ? *"
  then
        // Use the sendCommand method instead of the action
        Living_Temp_Numb.name.sendCommand(Living_Temp_Numb.state.toString)
end

//-----Rule to turn off/on AC (Auto)-----//
rule "Turn AC on if AC switch is ON and Living Temp is greater than Setpoint"
  when 
        Item Living_Temp_Numb received command or
        Item Living_Temp_Numb changed 
  then 
        if (AC.state==ON && Living_Temp_Numb.state > Setpoint_Temp.state) {
        if (AC_Relay.state == OFF) {
            AC_Relay.sendCommand(ON)
            timer = createTimer(now.plusSeconds(5), [ |
                Fan.sendCommand(ON)
                Fan_Relay.sendCommand(ON)
                timer = null
            ])
        }
    } else {
        if (AC_Relay.state == OFF) {
            AC_Relay.sendCommand(OFF)
            timer = createTimer(now.plusSeconds(5), [ |
                Fan.sendCommand(OFF)
                Fan_Relay.sendCommand(OFF)
                timer = null
            ])
        }
    }
        
  end

rule "Turn AC off if AC switch changes from ON to OFF"
  when 
        Item AC changed from ON to OFF
  then 
        Thread::sleep(2000) // Why do you sleep here????
        AC_Relay.sendCommand(OFF)
        timer = createTimer(now.plusSeconds(5), [ |
            Fan_Relay.sendCommand(OFF)
            Fan.sendCommand(OFF)
            timer = null
         ])
  end

Thanks for the help Vincent! So I removed the rule for updating the Living_Temp_Numb and replaced with checking the previousState in the rule. Again, any suggestions on ways to improve are welcomed!

Now I would like to add the following… but, I’m not sure how to implement it??

  1. Instead of turning ON the AC when Setpoint is < Living_Temp and OFF when Setpoint is reached… I’d like to do have a threshold of ± 1 degree. IE… if the Setpoint is 75 then the AC starts at 76 and turns off at 74. I tried the following, but it doesn’t work…

  2. I want to add another dht22 inside the supply air plenum so I can tell if the AC air is blowing cold enough AND so I can delay the fan shutting off until the delta between the Living_Temp and Supply_Air_Temp is less than, say, 10 degrees. I’m thinking I just need to create another rule to handle this?? Does this approach sound like the best solution or could I incorporate this in the rule I already have?

 if (AC.state==ON && Living_Temp_Numb.previousState(false, "influxdb").state > Setpoint_Temp.state + 1)
   }else{
        if (AC.state==ON && Living_Temp_Numb.previousState(false, "influxdb").state <= Setpoint_Temp.state - 1)
rule "Turn AC (ON) if Setpoint_Temp is < Living_Temp_Numb and (OFF) when Setpoint_Temp is reached"
  when
        Item Setpoint_Temp changed or
        Item Living_Temp_Numb changed
  then
        if (AC.state==ON && Living_Temp_Numb.previousState(false, "influxdb").state > Setpoint_Temp.state){
        if (AC_Relay.state == OFF && Fan_Relay.state == OFF){
                AC_Relay.sendCommand(ON)
                timer = createTimer(now.plusSeconds(2), [|
                Fan.sendCommand(ON)
                Fan_Relay.sendCommand(ON)
                System_Status_AC.sendCommand(ON)
                timer = null
                ])
                }
        }else{
        if (AC.state==ON && Living_Temp_Numb.previousState(false, "influxdb").state <= Setpoint_Temp.state){
        if (AC_Relay.state == ON && Fan_Relay.state == ON)
                AC_Relay.sendCommand(OFF)
                timer = createTimer(now.plusSeconds(2),[|
                Fan.sendCommand(OFF)
                Fan_Relay.sendCommand(OFF)
                System_Status_AC.sendCommand(OFF)
                timer = null
                ])
            }
        }
  end


rule "Turn AC off if AC switch changes from ON to OFF"
  when
        Item AC changed from ON to OFF
  then
        AC_Relay.sendCommand(OFF)
        timer = createTimer(now.plusSeconds(10), [|
                Fan_Relay.sendCommand(OFF)
                Fan.sendCommand(OFF)
                timer = null
                ])
  end

Thanks!!!

rule "Turn AC (ON) if Setpoint_Temp is < Living_Temp_Numb and (OFF) when Setpoint_Temp is reached"
when
      Item Setpoint_Temp changed or
      Item Living_Temp_Numb changed
then
    val temp = Living_Temp_Numb.state as Number
    val setPoint = SetPoint_Temp.state as Number
    if (AC.state==ON && (temp > (setPoint + 1))) {
        if (AC_Relay.state == OFF && Fan_Relay.state == OFF){
            AC_Relay.sendCommand(ON)
            timer = createTimer(now.plusSeconds(2), [|
                Fan.sendCommand(ON)
                Fan_Relay.sendCommand(ON)
               System_Status_AC.sendCommand(ON)
                timer = null
            ])
        }
    } else {
        if (AC.state==ON && (temp  <= (setPoint - 1))) {
            if (AC_Relay.state == ON && Fan_Relay.state == ON)
                AC_Relay.sendCommand(OFF)
                timer = createTimer(now.plusSeconds(2),[|
                    Fan.sendCommand(OFF)
                    Fan_Relay.sendCommand(OFF)
                    System_Status_AC.sendCommand(OFF)
                    timer = null
                 ])
            }
        }
    }
end

Thanks Vincent… I’ll give it a try when I have a little time.

Alright… I modified rule accordingly.

rule "Turn AC (ON) if Setpoint_Temp is < Living_Temp_Numb and (OFF) when Setpoint_Temp is reached"
  when
        Item Setpoint_Temp changed or
        Item AC changed or
        Item Living_Temp_Numb changed
  then
        val temp = Living_Temp_Numb.state as Number
        val setPoint = Setpoint_Temp.state as Number

        if (AC.state==ON && (temp > (setPoint + 1))){
        if (AC_Relay.state == OFF && Fan_Relay.state == OFF){
                AC_Relay.sendCommand(ON)
                timer = createTimer(now.plusSeconds(2), [|
                Fan.sendCommand(ON)
                Fan_Relay.sendCommand(ON)
                System_Status_AC.sendCommand(ON)
                timer = null
                ])
                }
        }else{
        if (AC.state==ON && (temp <= (setPoint -1))){
        if (AC_Relay.state == ON && Fan_Relay.state == ON){
                AC_Relay.sendCommand(OFF)
                timer = createTimer(now.plusSeconds(2),[|
                Fan.sendCommand(OFF)
                Fan_Relay.sendCommand(OFF)
                System_Status_AC.sendCommand(OFF)
                timer = null
                ])
                }
            }
        }
  end

Getting this in the openhab.log

2018-07-09 13:43:01.536 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'hvac.rules', using it anyway:
This expression is not allowed in this context, since it doesn't cause any side effects.
2018-07-09 13:43:01.644 [INFO ] [el.core.internal.ModelRepositoryImpl] - Refreshing model 'hvac.rules'

The rule is working… so I’m not sure if the log message is something I need to worry about??

Finally had a little time to troubleshoot the error… I commented everything out, then uncommented each rule individually until I found the problem. Turned out being something that didn’t have anything to do with the rule I thought it was.

Now I’m struggling to get the below to work reliably. Could someone please give me a hand? The ON/OFF operation works great. Just can’t get the notification part to work right. Appreciate it!

Items

//-----Proxy items used in rules-----//
Switch AC "AC" (HVAC)
Switch Heat "Heat" (HVAC)
Switch Fan "Fan" (HVAC)
Switch System_Status_AC "AC is...[%s]" (HVAC)
Switch System_Status_Heat "Heat is...[%s]" (HVAC)
Switch AC_Lockout

//-----Setpoint and temp/humidity sensorsp-----//
Number Setpoint_Temp "HVAC Setpoint [%.1f °F]" <temperature> (HVAC)
Number Living_Temp  "Main Temp [%.1f °F]" <temperature> (HVAC) { mqtt="<[OH2:tele/Living_Temp/SENSOR:state:JSONPATH($.AM2301.Temperature)]"}
Number Living_Hum  "Main Humidity [%.1f %%]" <temperature> (HVAC) { mqtt="<[OH2:tele/Living_Temp/SENSOR:state:JSONPATH($.AM2301.Humidity)]"}
Number SupplyAir_Temp  "Supply Temp [%.1f °F]" <temperature> (HVAC) { mqtt="<[OH2:tele/HVAC_SupplyTemp/SENSOR:state:JSONPATH($.AM2301.Temperature)]"}

//-----Relay Items used to turn on/off air handler-----//
Switch  AC_Relay   {gpio="pin:21 activelow:yes initialValue:high"}
Switch  Heat_Relay {gpio="pin:20 activelow:yes initialValue:high"}
Switch  Fan_Relay  {gpio="pin:16 activelow:yes initialValue:high"}

//-----Float switch in condensate drain line-----//
Contact AC_Float "AC Float" (HVAC) {gpio="pin:22 activelow:yes initialValue:high"}```


```php
Rules


var Timer timer=null
var Timer tempTimer=null
var Timer lockout=null

rule "Turn AC (ON) if Setpoint_Temp is < Living_Temp_Numb and (OFF) when Setpoint_Temp is reached"
  when
        Item Setpoint_Temp changed or
        Item AC changed or
        Item Living_Temp changed or
        Item Living_Hum changed
  then
        val temp = Living_Temp.state as Number
        val setPoint = Setpoint_Temp.state as Number

                if (AC_Lockout.state==OFF && AC.state==ON && (temp > (setPoint + 1))){
                if (AC_Relay.state==OFF && Fan_Relay.state==OFF){
                        AC_Relay.sendCommand(ON)
                        timer=createTimer(now.plusSeconds(2), [|
                        Fan.sendCommand(ON)
                        Fan_Relay.sendCommand(ON)
                        System_Status_AC.sendCommand(ON)
                        timer=null
                        ])
                }
        }else{
                if (AC.state==ON && (temp<=(setPoint -1))){
                if (AC_Relay.state==ON && Fan_Relay.state==ON){
                        AC_Relay.sendCommand(OFF)
                        timer=createTimer(now.plusSeconds(2), [|
                        Fan_Relay.sendCommand(OFF)
                        Fan.sendCommand(OFF)
                        System_Status_AC.sendCommand(OFF)
                        timer=null
                        ])
                        }
                }
        }
  end

rule "Turn AC off if AC switch changes from ON to OFF"
  when
        Item AC changed from ON to OFF
  then
        AC_Relay.sendCommand(OFF)
        timer=createTimer(now.plusSeconds(2), [|
        Fan_Relay.sendCommand(OFF)
        Fan.sendCommand(OFF)
        System_Status_AC.sendCommand(OFF)
        timer=null
        ])
  end
rule "Send Email/Notification after AC is ON for 15 mins IF split is less than 15 degrees"
  when
        Item AC_Relay changed from OFF to ON
  then
                val temp=Living_Temp.state as Number
                val supplyTemp=SupplyAir_Temp.state as Number
                tempTimer=createTimer(now.plusMinutes(20), [|
                        if(AC_Relay.state==ON && (temp-supplyTemp) < 15){
                        sendMail("XXX", "XXX", "XXX")
                        tempTimer=null
                                        }
                                }
                        ])
  end

rule "Turn off Heat if AC is ON"
    when
        Item Heat changed from OFF to ON
    then
        if(AC.state==ON){
        Heat.sendCommand(OFF)
        }
    end

rule "Turn off AC if Heat is ON"
    when
        Item AC changed from OFF to ON
    then
        if(Heat.state==ON){
        AC.sendCommand(OFF)
        }
    end

rule "Lock out AC when AC_Relay turns off"
    when
        Item AC_Relay changed from ON to OFF
    then
        AC_Lockout.sendCommand(ON)
    end

  rule "Unlock AC after 5 mins"
    when
        Item AC_Lockout changed from OFF to ON
    then
        lockout=createTimer(now.plusMinutes(5), [|
        AC_Lockout.sendCommand(OFF)
        ])
    end

What’s not working?
What are the error messages?

Vincent,

Sorry… I should have been more specific.

  1. It’s not that it isn’t working, it’s I’m not getting the results that I want. I’m getting email’s when I shouldn’t be. The set point is at 75 and the supply air temp is at 56, but I’m getting an email saying the ac split is low. The goal is to get a notification when there is a problem with the ac system. For example… low on freon, etc.

Here is a little explanation of what I am trying to do…

  1. When to AC system comes on, start a timer for a certain period of time say 20 mins
  2. After 20 mins, if the AC_Relay is still ON subtract the val supplyTemp for the val temp. If the difference is less than 15 send me an email.

Let’s say the set point is at 75 degrees and after 20 mins the supply air temp is at 55 degrees (around 20 degree difference is what it should be)… I don’t want to get notified. Now, let’s say again the set point is at 75 degrees, but now after 20 mins the supply air temp is at 65 degrees… I want a notification saying the AC split is low.

  1. I thought I posted the logs above, apparently not. Sorry it was late, I’d been banging my head on my desk, and I was tired!! Please see below… So I made some changes after posting the above and the error messages have gone away, but now they are back. I have no idea???

openhab.log

2018-07-19 17:41:17.658 [ERROR] [org.quartz.core.JobRunShell         ] - Job DEFAULT.2018-07-19T17:41:17.653-05:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: [ | {
  org.eclipse.xtext.xbase.impl.XIfExpressionImpl@177592d
} ] threw an unhandled Exception: 
java.lang.NullPointerException: null
	at org.eclipse.smarthome.model.script.engine.ScriptError.<init>(ScriptError.java:66) [137:org.eclipse.smarthome.model.script:0.10.0.oh230]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:132) [137:org.eclipse.smarthome.model.script:0.10.0.oh230]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:901) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:864) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:223) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:219) [137:org.eclipse.smarthome.model.script:0.10.0.oh230]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:759) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:219) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:219) [137:org.eclipse.smarthome.model.script:0.10.0.oh230]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluateArgumentExpressions(XbaseInterpreter.java:1115) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._invokeFeature(XbaseInterpreter.java:1045) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeFeature(XbaseInterpreter.java:991) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:143) [137:org.eclipse.smarthome.model.script:0.10.0.oh230]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:901) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:225) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:219) [137:org.eclipse.smarthome.model.script:0.10.0.oh230]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluateArgumentExpressions(XbaseInterpreter.java:1115) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._invokeFeature(XbaseInterpreter.java:1045) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeFeature(XbaseInterpreter.java:991) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:143) [137:org.eclipse.smarthome.model.script:0.10.0.oh230]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:901) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:225) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:219) [137:org.eclipse.smarthome.model.script:0.10.0.oh230]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:457) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:243) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:219) [137:org.eclipse.smarthome.model.script:0.10.0.oh230]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:446) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:227) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:219) [137:org.eclipse.smarthome.model.script:0.10.0.oh230]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:189) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:29) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at com.sun.proxy.$Proxy157.apply(Unknown Source) [?:?]
	at org.eclipse.smarthome.model.script.internal.actions.TimerExecutionJob.execute(TimerExecutionJob.java:49) [137:org.eclipse.smarthome.model.script:0.10.0.oh230]
	at org.quartz.core.JobRunShell.run(JobRunShell.java:202) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh230]
	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh230]
2018-07-19 17:41:17.680 [ERROR] [org.quartz.core.ErrorLogger         ] - Job (DEFAULT.2018-07-19T17:41:17.653-05:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: [ | {
  org.eclipse.xtext.xbase.impl.XIfExpressionImpl@177592d
} ] threw an exception.
org.quartz.SchedulerException: Job threw an unhandled exception.
	at org.quartz.core.JobRunShell.run(JobRunShell.java:213) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh230]
	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh230]
Caused by: java.lang.NullPointerException
	at org.eclipse.smarthome.model.script.engine.ScriptError.<init>(ScriptError.java:66) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:132) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:901) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:864) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:223) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:219) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:759) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:219) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:219) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluateArgumentExpressions(XbaseInterpreter.java:1115) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._invokeFeature(XbaseInterpreter.java:1045) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeFeature(XbaseInterpreter.java:991) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:143) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:901) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:225) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:219) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluateArgumentExpressions(XbaseInterpreter.java:1115) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._invokeFeature(XbaseInterpreter.java:1045) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeFeature(XbaseInterpreter.java:991) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:143) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:901) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:225) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:219) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:457) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:243) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:219) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:446) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:227) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:219) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:189) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:29) ~[?:?]
	at com.sun.proxy.$Proxy157.apply(Unknown Source) ~[?:?]
	at org.eclipse.smarthome.model.script.internal.actions.TimerExecutionJob.execute(TimerExecutionJob.java:49) ~[?:?]
	at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[?:?]
	... 1 more

Ok, I got it, I’ll have a look in the morning UK

Thank you Vincent!

Once I get this working good… my plan is to share it with the community so that other’s may benefit from it. I want it to be right and test it for a month or two before I do so though.

In the email rule there is a bracket too many:
I also changed your indents and added spaces for readability.

Move the values temp and supplyTemp in the timer lamdba or else you will have values 20 minutes old


rule "Send Email/Notification after AC is ON for 15 mins IF split is less than 15 degrees"
when
    Item AC_Relay changed from OFF to ON
then
    tempTimer = createTimer(now.plusMinutes(20), [ |
        val temp = Living_Temp.state as Number
        val  supplyTemp = SupplyAir_Temp.state as Number
        if (AC_Relay.state == ON && (temp - supplyTemp) < 15) {
            sendMail("XXX", "XXX", "XXX")
            tempTimer = null
        }
    ])
end

Thank you Vincent for taking the time to help me out! I don’t have much programming experience, but I’m learning, for sure.

I actually thought about and did exactly that about an hour ago. I also added logInfo to the rule so I could step through it. Man does it help!

What I did is a little different than your solution… here it is.

when
    Item AC_Relay changed from OFF to ON
then
    val set = Setpoint_Temp.state as Number
    val  threshold = 15
    tempTimer = createTimer(now.plusMinutes(20), [ |
        if(AC_Relay.state==ON){
        val supply = SupplyAir_Temp.state as Number
            if(threshold >=(set-supply)){
            sendMail("XXX", "XXX", "XXX")
            tempTimer = null
            }
        }
    ])
end

Now I getting this error regarding the AC lockout rule. I’m trying to figure it out as we speak at 1:30 am US time! If you have any ideas… I’m all ears.

rule

rule "Lock out AC when AC_Relay turns off"
    when
        Item AC_Relay changed from ON to OFF
    then
        Lockout_AC.sendCommand(ON)
        logInfo("Lockout", "AC lockout ON")
    end

  rule "Unlock AC after 5 mins"
    when 
        Item Lockout_AC changed from OFF to ON
    then
        logInfo("Lockout", "Starting 5 min lockout timer")
        lockoutTimer=createTimer(now.plusMinutes(5),[|
        logInfo("Lockout", "5 min lockout is up")
        Lockout_AC.sendCommand(OFF)
        logInfo("Lockout", "AC lockout OFF")
        lockoutTimer=null
        ])
    end

openhab.log

2018-07-20 01:16:16.832 [INFO ] [pse.smarthome.model.script.TempCheck] - AC Relay is ON
2018-07-20 01:16:16.839 [INFO ] [pse.smarthome.model.script.TempCheck] - Temp minus Supply is less than or equal 15
2018-07-20 01:20:50.865 [INFO ] [el.core.internal.ModelRepositoryImpl] - Refreshing model 'hvac.rules'
2018-07-20 01:22:16.829 [INFO ] [lipse.smarthome.model.script.Lockout] - AC lockout ON
2018-07-20 01:22:16.840 [INFO ] [lipse.smarthome.model.script.Lockout] - Starting 5 min lockout timer
2018-07-20 01:23:35.707 [INFO ] [el.core.internal.ModelRepositoryImpl] - Refreshing model 'hvac.rules'
2018-07-20 01:27:16.844 [INFO ] [lipse.smarthome.model.script.Lockout] - 5 min lockout is up
2018-07-20 01:27:16.848 [ERROR] [org.quartz.core.JobRunShell         ] - Job DEFAULT.2018-07-20T01:27:16.843-05:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: [ | {
  logInfo(<XStringLiteralImpl>,<XStringLiteralImpl>)
  <XFeatureCallImplCustom>.sendCommand(<XFeatureCallImplCustom>)
  logInfo(<XStringLiteralImpl>,<XStringLiteralImpl>)
  <null>.lockoutTimer = <XNullLiteralImplCustom>
} ] threw an unhandled Exception: 
java.lang.NullPointerException: null
	at org.eclipse.smarthome.model.script.engine.ScriptError.<init>(ScriptError.java:66) [137:org.eclipse.smarthome.model.script:0.10.0.oh230]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:132) [137:org.eclipse.smarthome.model.script:0.10.0.oh230]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:901) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:864) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:223) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:219) [137:org.eclipse.smarthome.model.script:0.10.0.oh230]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluateArgumentExpressions(XbaseInterpreter.java:1115) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._invokeFeature(XbaseInterpreter.java:1045) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeFeature(XbaseInterpreter.java:991) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:143) [137:org.eclipse.smarthome.model.script:0.10.0.oh230]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:763) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:219) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:219) [137:org.eclipse.smarthome.model.script:0.10.0.oh230]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:446) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:227) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:219) [137:org.eclipse.smarthome.model.script:0.10.0.oh230]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:189) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:29) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
	at com.sun.proxy.$Proxy157.apply(Unknown Source) [?:?]
	at org.eclipse.smarthome.model.script.internal.actions.TimerExecutionJob.execute(TimerExecutionJob.java:49) [137:org.eclipse.smarthome.model.script:0.10.0.oh230]
	at org.quartz.core.JobRunShell.run(JobRunShell.java:202) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh230]
	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh230]
2018-07-20 01:27:16.853 [ERROR] [org.quartz.core.ErrorLogger         ] - Job (DEFAULT.2018-07-20T01:27:16.843-05:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: [ | {
  logInfo(<XStringLiteralImpl>,<XStringLiteralImpl>)
  <XFeatureCallImplCustom>.sendCommand(<XFeatureCallImplCustom>)
  logInfo(<XStringLiteralImpl>,<XStringLiteralImpl>)
  <null>.lockoutTimer = <XNullLiteralImplCustom>
} ] threw an exception.
org.quartz.SchedulerException: Job threw an unhandled exception.
	at org.quartz.core.JobRunShell.run(JobRunShell.java:213) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh230]
	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh230]
Caused by: java.lang.NullPointerException
	at org.eclipse.smarthome.model.script.engine.ScriptError.<init>(ScriptError.java:66) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:132) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:901) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:864) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:223) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:219) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluateArgumentExpressions(XbaseInterpreter.java:1115) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._invokeFeature(XbaseInterpreter.java:1045) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeFeature(XbaseInterpreter.java:991) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:143) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:763) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:219) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:219) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:446) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:227) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:219) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:189) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:29) ~[?:?]
	at com.sun.proxy.$Proxy157.apply(Unknown Source) ~[?:?]
	at org.eclipse.smarthome.model.script.internal.actions.TimerExecutionJob.execute(TimerExecutionJob.java:49) ~[?:?]
	at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[?:?]
	... 1 more

What it the purpose of the lockout?
Do you ALWAYS turn it OFF after 5 mins ON?

Tidied up and added spaces.
You need a space after [ and before | depending on what version OH you are running?

rule "Unlock AC after 5 mins"
when 
    Item Lockout_AC changed from OFF to ON
then
    logInfo("Lockout", "Starting 5 min lockout timer")
    lockoutTimer = createTimer(now.plusMinutes(5), [ |
        logInfo("Lockout", "5 min lockout is up")
        Lockout_AC.sendCommand(OFF)
        logInfo("Lockout", "AC lockout OFF")
        lockoutTimer = null
    ])
end

The purpose is to keep the compressor from short cycling. For example… say I have the set point at 75. The system turns off. Then I bump the temp down to 70… I want to wait for 5 mins before the system will come on again. It a protection mechanism to keep from burning up the compressor.

Yes, every time the AC_Relay changes from ON to OFF.

Build 2.3.0

Taking a look now…

Ok then use the expire binding instead, very simple.
Install the expire binding: https://www.openhab.org/addons/bindings/expire1/

Change the Lockout_AC item:

Switch Lockout_AC { expire="5m, command=OFF" }

Remove the rule.
The switch will get a command OFF 5 minutes after being switched on. Every time.