Avoiding a rule race condition

I have the following rule which syncs 2 devices. Most of the time, this works great except some times the lights start bouncing on/off. I can stop this by disabling/enabling the rule which seems to reset the condition.

Details:

  1. I have two switches though only one is the master switch (Switch A). The second switch (Switch B) controls the device but the switch itself is not physically reachable and thus I rely on the Switch A to control the Switch B’s load.

  2. The rule (below) monitors the state change of Switch A … first if Switch A is turned on and thus turns Switch B off, and then does the same for an off condition.

  3. In general, this works fine. Except occasionally we seem to ping pong … on and off … on and off until I disable the rule from the API.

  4. On thing that I have observed is that it will take the Rule disable several seconds before the blinking stops. I suspect that multiple rules are fired off which may be the cause.

  5. I tried to mitigate this by writing my driver to ignore an “on” or “off” command if the switch is already on or off. That way, I am not generating useless events.

While this works in general, we have the occasional blinking action. Of course, it happens at inopportune times when going to bed or leaving the house. Getting to the computer to remedy the rule is not convenient.

Any ideas on how I can change the rule to avoid the race condition. I again suspect that multiple threads of the rules are fired off when this condition occurs but there is no way to verify this. I am not aware of how to test if one copy of the rule thread is currently active so that I could stop the loop.

Here’s the rule as it exists today:

{
  "if": {
    "changes": {
      "equals": {
        "left": {
          "device": {
            "devices": [
              "ec735f32-1b14-4463-837e-c86b5f07272c"
            ],
            "component": "switch2",
            "capability": "switch",
            "attribute": "switch"
          }
        },
        "right": {
          "string": "on"
        }
      }
    },
    "then": [
      {
        "command": {
          "devices": [
            "7d81640d-d222-4711-85a2-d8fb3acce457"
          ],
          "commands": [
            {
              "component": "main",
              "capability": "switch",
              "command": "on"
            }
          ]
        }
      }
    ],
    "else": [
      {
        "if": {
          "changes": {
            "equals": {
              "left": {
                "device": {
                  "devices": [
                    "ec735f32-1b14-4463-837e-c86b5f07272c"
                  ],
                  "component": "switch2",
                  "capability": "switch",
                  "attribute": "switch"
                }
              },
              "right": {
                "string": "off"
              }
            }
          },
          "then": [
            {
              "command": {
                "devices": [
                  "7d81640d-d222-4711-85a2-d8fb3acce457"
                ],
                "commands": [
                  {
                    "component": "main",
                    "capability": "switch",
                    "command": "off"
                  }
                ]
              }
            }
          ]
        }
      }
    ]
  }
}

Is that possible to do these 2 rules ?

  • If switch A changes AND switch A is different of switch B, then turn switch B the same as switch A
  • If switch B changes AND switch A is different of switch B, then turn switch A the same as switch B

If so, then it will stop the race.

1 Like

I have done similar using multiple actions

{
  "name": "Switch A -> Switch B",
  "actions": [
    {
      "if": {
        "equals": {
          "right": {
            "device": {
              "devices": [
                "Switch A ID"
              ],
              "component": "main",
              "capability": "switch",
              "attribute": "switch"
            }
          },
          "left": {
            "string": "on"
          }
        },
        "then": [
          {
            "command": {
              "devices": [
				"Switch B ID"
              ],
              "commands": [
                {
                  "component": "main",
                  "capability": "switch",
                  "command": "on"
                }
              ]
            }
          }
        ]
      }
    },
    {
      "if": {
        "equals": {
          "right": {
            "device": {
              "devices": [
                "Switch A ID"
              ],
              "component": "main",
              "capability": "switch",
              "attribute": "switch"
            }
          },
          "left": {
            "string": "off"
          }
        },
        "then": [
          {
            "command": {
              "devices": [
				"Switch B ID"
              ],
              "commands": [
                {
                  "component": "main",
                  "capability": "switch",
                  "command": "off"
                }
              ]
            }
          }
        ]
      }
    }
  ]
}

Is this action what you wanted?

Are the switches Z-Wave devices? If so, you could simply use Association Groups to mirror Switch A actions to Switch B.

If not, then with Routines, I would use preconditions to check the state of Switch B like this:

If Switch B Off (precondition)
Switch A On
Then
Switch B On

If Switch B On (precondition)
Switch A Off
Then
Switch B Off

That is an interesting one. Stopping the light turning on and off is one thing, but that doesn’t mean the Rule isn’t still looping, and why on earth is it looping in the first place?

Out of curiosity, have you tried the Rule without using the changes conditions? By that I mean just using equals without wrapping them in changes. I’d imagine they are redundant but I’d also expect them to be harmless.

Following up on my earlier post, here is an example of the Routine I suggested:

and the corresponding JSON representation of the Routine:

{
  "if": {
    "and": [
      {
        "equals": {
          "left": {
            "device": {
              "devices": [
                "55813903-c855-4234-9ae4-0b6d50905cd0"
              ],
              "component": "main",
              "capability": "switch",
              "attribute": "switch",
              "trigger": "Never"
            },
            "type": "device"
          },
          "right": {
            "string": "off",
            "type": "string"
          },
          "changesOnly": false
        },
        "type": "equals"
      },
      {
        "equals": {
          "left": {
            "device": {
              "devices": [
                "1f8b4244-5e1a-49ac-a8c7-9b8cfc6ee40f"
              ],
              "component": "main",
              "capability": "switch",
              "attribute": "switch",
              "trigger": "Always"
            },
            "type": "device"
          },
          "right": {
            "string": "on",
            "type": "string"
          },
          "changesOnly": false
        },
        "type": "equals"
      }
    ],
    "type": "and",
    "then": [
      {
        "command": {
          "devices": [
            "55813903-c855-4234-9ae4-0b6d50905cd0"
          ],
          "commands": [
            {
              "component": "main",
              "capability": "switch",
              "command": "on"
            }
          ]
        },
        "type": "command"
      }
    ],
    "sequence": {
      "then": "Parallel",
      "else": "Parallel"
    }
  },
  "type": "if"
}

Routine JSON syntax differs slightly from Rules, but this might help you figure out how to modify your existing Rules.