Design Pattern: Hysteresis

Edit: Updated for OH 4

Please see Design Pattern: What is a Design Pattern and How Do I Use Them for a desciption of DPs.

Problem Statement

Often one will encounter a situation where a device needs to be turned on/off based on a sensor reading but sensors are inherently noisy, sometimes jumping up a little bit and then jumping down a little bit on each reading.

In the naive approach one sets a threshold with a simple:

var newCommand = 'OFF';
if(items.MySensor.quantityState.greaterThan(items.MySetpoint.quantityState)) {
  newCommand = 'ON';
}
items.MyDevice.sendCommand(newCommand);

However, when using this approach. if the sensor happens to be sitting around the setpoint value, it will bounce above and below rapidly causing the device to rapidly turn on and off. This is often undesireable.

Concept

Provide a “dead zone”, called hysteresis, in which changes in the sensor reading does not cause a change in the device.

For example, let’s say we have a heater with a setpoint of 70 °F. When the temperature is below 70 °F we want to turn on the heater and when it’s above 70 °F we want to turn it off.

Hysteresis Profile

Link the temperature sensor Channel to a Switch Item and on the link apply the hysteresis profile. This profile will implement the hysteresis, commanding the Switch to ON or OFF as configured.

JavaScript

var newCommand = 'STAY';
if(items.MyTemp.quantityState.lessThan(Quantity('68 °F'))) {
  newCommand = 'ON';
}
else if(items.MyTemp.quantityState.greaterThan(Quantity('70 °F'))) {
  newCommand = 'OFF';
}
if(newCommand != 'STAY') {
  items.MyHeater.sendCommand(newCommand);
}

Note, the code could be made more concise as the following.

if(items.MyTemp.quantityState.lessThan(Quantity('68 °F'))) {
  items.MyHeater.sendCommand('ON');
else if(items.MyTemp.quantityState.greaterThan(Quantity('70 °F'))) {
  items.MyHeater.sendCommand('OFF');
}

But, by waiting to the end of the rule to send the command in only one place, you have the opportunity to add additional logic without repeating it. For example, to only command the heater if it’s not already in the desired state:

var newCommand = 'STAY';
if(items.MyTemp.quantityState.lessThan(Quantity('68 °F'))) {
  newCommand = 'ON';
}
else if(items.MyTemp.quantityState.greaterThan(Quantity('70 °F'))) {
  newCommand = 'OFF';
}
if(newCommand != 'STAY' && newCommand != items.MyHeater.state) {
  items.MyHeater.sendCommand(newCommand);
}

Advantages and Disadvantages

There are all sorts of approaches to keep a device from flapping. This approach works best when the sensor is measuring something that tends to change relatively slowly like temperature or humidity. The wider the hysteresis buffer, the longer it should take for a device to turn on or off as the sensor reading will take more time to reach the lower or upper thresholds.

10 Likes

What else to say but, another great dp.

Eventually use the (new) hysteresis profile instead ?

That works in some cases but not all cases so I intend to mention the profile but keep the DP in place.

2 Likes

Hello,

I am wondering if the DSL rule shall not be modifed as follows:

if(MyTemp.state < 68 && MyHeater.state != ON) MyHeater.sendCommand(ON)
else if(MyTemp.state >= 70 && MyHeater.state != OFF) MyHeater.sendCommand(OFF)

in order to limit trigering commands every time the temperature sensor changes below 68 (ex 67, 67.5, 67,7 etc)?

All things are possible. The main point is that there is a range of values where nothing happens so that the device doesn’t bounce. The DPs by necessity for clarity, only shows the bare minimum to explain the concept usually.

Note, this case might be better handled using the hysteresis profile instead of a rule.

Interesting, is the hysteresis profile a specific binding restricted (ex openweathermap) or can it be used for any channel/binding (ex modbus)?

See Items | openHAB. Profiles are applied to the link between a Channel and an Item.