How to make rules easy?

Tags: #<Tag:0x00007f616e7ecb20> #<Tag:0x00007f616e7ec738> #<Tag:0x00007f616e7ec3a0>

Hello eveybody,

At first my English is not the best… sorry.

A few weeks before I started to use OH for my Home automation.

At the moment i write a few rules to control my rollershutter and heating. My code works fine, but I think there must be an easier way to control it.

Here is the code for rollershutter controlling

rule RolladenLivingToggleOn
when
 Item FF_LivingRoom_Roll received command UP
then
 sendHttpGetRequest("http://myIP/cm?cmnd=Pulsetime1%20128")
 sendHttpGetRequest("http://myIP/cm?cmnd=Power1%20TOGGLE")
end

rule RolladenLivingToggleOff
when
 Item FF_LivingRoom_Roll received command DOWN
then
 sendHttpGetRequest("http://myIP/cm?cmnd=Pulsetime2%20128")
 sendHttpGetRequest("http://myIP/cm?cmnd=Power2%20TOGGLE")
end

rule RolladenLivingToggleOff
when
 Item FF_LivingRoom_Roll received command STOP
then
 sendHttpGetRequest("http://myIP/cm?cmnd=Power1%20OFF")
 sendHttpGetRequest("http://myIP/cm?cmnd=Power2%20OFF")
end

I control the rollershuter with a Sonoff T1 (2way). Is there a possibility to make the rule shorter / easier?

The second is the controlling of my MAX! heating thermostats, especially the heating-mode

rule DiningChangeMode
when
    Item FF_Dining_Mode changed
then
    switch (FF_Dining_Mode) {
        case 0 : sendCommand(FF_Dining_Mode, "AUTOMATIC")
        case 1 : sendCommand(FF_Dining_Mode, "MANUAL")
        case 2 : sendCommand(FF_Dining_Mode, "BOOST")
    }
end

rule LivingWChangeMode
when
    Item FF_LivingRoom_ModeW changed
then
    switch (FF_LivingRoom_ModeW) {
        case 0 : sendCommand(FF_LivingRoom_ModeW, "AUTOMATIC")
        case 1 : sendCommand(FF_LivingRoom_ModeW, "MANUAL")
        case 2 : sendCommand(FF_LivingRoom_ModeW, "BOOST")
    }
end

There are more than 15 thermostats in my home, and at the moment I use a single rule for every thermostat. Can I do this with only 1 rule?

Thanks
Daniel

First, some approaches for reference.

Rollershutter Rules

Let’s:

  • collapse the three rules into one rule
  • apply an approach where we calculate the thing to be done then do the thing to structure the rule
rule "RolladenLiving command"
when
    Item FF_LivingRoom_Roll received command
then
    // Calculate what to do
    var String pulse = null
    var String power = null
    switch receivedCommand {
        case UP: {
            pulse = "Pulsetime1%20128"
            power = "Power1%20TOGGLE"
        }
        case DOWN: {
            pulse = "Pulsetime2%20128"
            power = "Power2%20TOGGLE"
        }
        case STOP: {
            pulse = "Power1%20OFF"
            power = "Power2%20OFF"
        }
    }

    // do it
    sendHttpGetRequest("http://myIP/cm?cmnd="+pulse)
    sendHttpGetRequest("http://myIP/cm?cmnd="+power)
end

That’s a little better but we can make it a bit shorter. Let’s see if we can improve that initialization part.

rule "RolladenLiving command"
when
    Item FF_LivingRoom_Roll received command
then
    // Calculate what to do
    var String pulse = if(receivedCommand == STOP) "Power1%20OFF" else "Pulsetime1%20128"
    var String power = if(receivedCommand == STOP) "Power2%20OFF" else "Power1%20TOGGLE"

    if(receivedCommand == DOWN){
        pulse.replace("1", "2")
        power.replace("1", "2")
    }

    // do it
    sendHttpGetRequest("http://myIP/cm?cmnd="+pulse)
    sendHttpGetRequest("http://myIP/cm?cmnd="+power)
end

That is definitely shorter though it takes a bit more effort to understand how it works. For the record: initialize pulse and power using the command for the OFF case if the receivedCommand is STOP otherwise initialize it to the UP case. Check to see if the command was DOWN and if so modify pulse and power to cover that case.

You can make this rule generic by creating a mapping between your Item names and IP address (I assume each rollershutter has its own IP) and using the triggeringItem implicit variable:

rule "RolladenLiving command"
when
    Item FF_LivingRoom_Roll received command or
    Item Some_Other_Roll received command or
    ...
then
    // Calculate what to do
    val ip = transform("MAP", "rollershutters.map", triggeringItem.name)

    var String pulse = if(receivedCommand == STOP) "Power1%20OFF" else "Pulsetime1%20128"
    var String power = if(receivedCommand == STOP) "Power2%20OFF" else "Power1%20TOGGLE"

    if(receivedCommand == DOWN){
        pulse.replace("1", "2")
        power.replace("1", "2")
    }

    // do it
    sendHttpGetRequest("http://"+ip+"/cm?cmnd="+pulse)
    sendHttpGetRequest("http://"+ip+"/cm?cmnd="+power)
end

You will need to install the Map tranform and create a file rollershutters.map in your transform folder that looks like the following:

FF_LivingRoom_Roll=192.168.1.123
Some_Other_Roll=192.168.1.124

Yes, but your rules as written won’t work. Please post your Items so I can see what you are trying to do. First of all you need to call .state to get the state of an Item.

switch(FF_Dining_Mode.state)

Secondly, either FF_Dining_Mode is a Number in which case the case part of the switch statement is right and the sendCommand part is wrong, or it is a String Item and it is vise versa. You cannot send a String to a Number Item and you cannot use a Number to compare to the state of a String Item.

Thirdly it is always better to use the method instead of the action for sendCommand and postUpdate.

FF_Dining_Mode.sendCommand("AUTOMATIC")

Finally, assuming all of the above were not a problem and this rule actually ran, you will have created an infinite loop.

  1. FF_Dining_Mode changes
  2. Rule triggers
  3. sendCommand(FF_Dining_Mode, “some new state”)
  4. FF_Dining_Mode changes
  5. Rule triggers
    and so on

Or you have a situation where nothing happens.

  1. FF_Dining_Mode changes
  2. Rule triggers
  3. sendCommand(FF_Dining_Mode", “the same state it already is in”)

And nothing happens. The only thing the rule does is send a command and since the command is the same as what the Item already is in this rule does effectively nothing.

The Item that triggers the Rule must be different than the Items you are sending commands to when the Rule triggers or else you end up doing nothing at all or stuck in an infinite loop.

1 Like

At first… the code for the rollershutters works fine… thanks

The code i explain works… the thermostats changing the mode as i want to auto, manuell or boost

here are the item…

String   FF_LivingRoom_ModeW       	    "Modus Wohnzimmer [MAP(maxcubeMode.map):%s]"    <heating>       (gHeatingMode)                                      {channel="max:thermostat:KEQ0538054:OEQ1134569:mode"}

the mapping…

AUTOMATIC=AUTO
MANUAL=Manuell
BOOST=BOOST

and the sitemap-code…

Setpoint item=FF_Dining_Heating 			label="Esszimmer" 			step=0.5 minValue=4.5 maxValue=30.0  	    
Selection item=FF_Dining_Mode											mappings=[AUTOMATIC="Auto", MANUAL="Manuell", BOOST="Boost"]
Setpoint item=FF_LivingRoom_HeatingW 		label="Wohnzimmer"			step=0.5 minValue=4.5 maxValue=30.0
Selection item=FF_LivingRoom_ModeW										mappings=[AUTOMATIC="Auto", MANUAL="Manuell", BOOST="Boost"]

The last example with the mapping doesn´t work. The log shows this lines

20:54:43.514 [INFO ] [marthome.event.ItemStateChangedEvent] - FF_LivingRoom_Klight changed from ON to OFF
20:54:44.239 [INFO ] [smarthome.event.ItemCommandEvent    ] - Item 'FF_LivingRoom_Roll' received command UP
20:54:44.239 [ERROR] [.script.engine.ScriptExecutionThread] - Rule 'RolladenLiving command': An error occurred during the script execution: The name 'triggeringItem' cannot be resolved to an item or type.

What version of OH are you running? I assumed at least version 2.2 but if you don’t have triggeringItem then you must be on an earlier version. I strong encourage you to upgrade if this is the case.

I need more details. If FF_Dining_Heating changes then you want to set the FF_Dining_Mode? Or do you want to set the FF_Dining_Heating when you change FF_Dining_Mode?

In either case, your existing change mode rules make no sense.

Walk through it step by step. You change X Item on the sitemap and Y happens.

I’m runing OH 2.1…

The Dining Mode is to setup the thermostats in the automatic-mode (with programmed timetables), manuell (without) or boost mode (open the thermostat to maximum for 300 seconds).
All of this modes have nothing to do with the actual heating temperature.

This temperatures i simply set over setpoint items…

I think it will be the same probleme, because i think that the rule would need also triggering item…

But what does that mean in terms of commands sent to Items.

What should happen when you set FF_Dining_Mode to Boost in terms of commands sent to other Items? What command(s) need to be sent to what Item(s) to “open the thermostat to maximum for 300 seconds”? The answer to that question will tell you what the rule needs to do.

Yes, to make this rule generic it will require triggeringItem. There is a hack that usually works but it requires persistence and a number of other preconditions. I strongly recommend upgrading over trying to use the persistence hack.

I only try to make one rule instead of 15 rules… the code of alle rules is the same for each thermostat but not the item name

rule DiningChangeMode
when
    Item FF_Dining_Mode changed
then
    switch (FF_Dining_Mode) {
        case 0 : sendCommand(FF_Dining_Mode, "AUTOMATIC")
        case 1 : sendCommand(FF_Dining_Mode, "MANUAL")
        case 2 : sendCommand(FF_Dining_Mode, "BOOST")
    }
end

rule LivingWChangeMode
when
    Item FF_LivingRoom_ModeW changed
then
    switch (FF_LivingRoom_ModeW) {
        case 0 : sendCommand(FF_LivingRoom_ModeW, "AUTOMATIC")
        case 1 : sendCommand(FF_LivingRoom_ModeW, "MANUAL")
        case 2 : sendCommand(FF_LivingRoom_ModeW, "BOOST")
    }
end

rule LivingKChangeMode
when
    Item FF_LivingRoom_ModeK changed
then
    switch (FF_LivingRoom_ModeK) {
        case 0 : sendCommand(FF_LivingRoom_ModeK, "AUTOMATIC")
        case 1 : sendCommand(FF_LivingRoom_ModeK, "MANUAL")
        case 2 : sendCommand(FF_LivingRoom_ModeK, "BOOST")
    }
end

rule KitchenChangeMode
when
    Item FF_Kitchen_Mode changed
then
    switch (FF_Kitchen_Mode) {
        case 0 : sendCommand(FF_Kitchen_Mode, "AUTOMATIC")
        case 1 : sendCommand(FF_Kitchen_Mode, "MANUAL")
        case 2 : sendCommand(FF_Kitchen_Mode, "BOOST")
    }
end

rule BathroomUChangeMode
when
    Item FF_Bathroom_Mode changed
then
    switch (FF_Bathroom_Mode) {
        case 0 : sendCommand(FF_Bathroom_Mode, "AUTOMATIC")
        case 1 : sendCommand(FF_Bathroom_Mode, "MANUAL")
        case 2 : sendCommand(FF_Bathroom_Mode, "BOOST")
    }
end

// and so on 9 more thermostats

When i tried to update OH to version 2.2 4 weeks before the system was very unstable

Then I would recommend waiting until you upgrade to 2.3 release before tackling these problems. I just cannot recommend the persistence hack any longer.

But if you want to give it a try look at the Working with Groups in Rules DP I linked to above.

That does not answer my question. I need:

  1. FF_Dining_Mode changes to “BOOST”
  2. FF_Dining_Heating changes to 22.5
  3. FF_Dining_Heating_Switch changes to ON
  4. Five minutes later
  5. FF_Dining_HEating changes to 22.0
  6. FF_Dining_Heating_Switch changes to OFF

I’m certain that the above sequence is incorrect. YOU need to tell me what the correct sequence of events are. Your current rule, even if you duplicate it over and over for 15 thermostats, is nonsensical. It doesn’t do anything in the best case and causes an infinite loop in the worst case.

Must gone back to the first topic…

I have updated to OH2.2… all works fine with the triggering item. The rule knows what item I pushed.

But nothing happens at all… I mean the http-command will not been send…

whats wrong…

here the code

rule "Rolladen command"
when
   Item FF_LivingRoom_Roll received command or
        GF_Hallway_Roll received command
then
  //Calculate what to do
   val ip = transform("MAP", "rollershutters.map", triggeringItem.name)
   var String pulse = if(receivedCommand == STOP) "Power1%20OFF" else "Pulsetime1%20128"
   var String power = if(receivedCommand == STOP) "Power2%20OFF" else "Power1%20TOGGLE"

   if(receivedCommand == DOWN){
       pulse.replace("1", "2")
       power.replace("1", "2")
   }

   //do it
   sendHttpGetRequest("http://"+ip+"/cm?cmnd="+pulse)
   sendHttpGetRequest("http://"+ip+"/cm?cmnd="+power)
end

By logging this appears

13:29:33.605 [WARN ] [del.core.internal.ModelRepositoryImpl] - Configuration model 'haus.rules' is either empty or cannot be parsed correctly!
13:29:33.698 [WARN ] [del.core.internal.ModelRepositoryImpl] - Configuration model 'haus.rules' has errors, therefore ignoring it: [28,9]: no viable alternative at input 'GF_Hallway_Roll'
13:31:32.560 [INFO ] [smarthome.event.ItemCommandEvent     ] - Item 'FF_LivingRoom_Roll' received command DOWN
13:31:32.560 [INFO ] [smarthome.event.ItemStateChangedEvent] - FF_LivingRoom_Roll changed from 0 to 100
13:32:03.019 [INFO ] [smarthome.event.ItemStateChangedEvent] - Date_today changed from 2018-04-06T13:31:03.012+0200 to 2018-04-06T13:32:03.019+0200
13:32:23.571 [INFO ] [smarthome.event.ItemCommandEvent     ] - Item 'GF_Hallway_Roll' received command DOWN
13:32:23.586 [INFO ] [smarthome.event.ItemStateChangedEvent] - GF_Hallway_Roll changed from 0 to 100
13:32:24.156 [INFO ] [smarthome.event.ItemCommandEvent     ] - Item 'GF_Hallway_Roll' received command UP
13:32:24.171 [INFO ] [smarthome.event.ItemStateChangedEvent] - GF_Hallway_Roll changed from 100 to 0

If these rules are in haus.rules then the problem is you have a syntax error on line 29 column 9.

Because of the syntax error the entire .rules file is rejected.

Looking at what you posted you are missing the leading Item for the rule trigger.

when
   Item FF_LivingRoom_Roll received command or
   Item GF_Hallway_Roll received command
then

oh sh…
I’m stupid
Thx

it works…