OpenHab Persistence Tutorial: Using Persistence in Your Rules
Augment your smart home project by learning about the importance of persistence in rules.
Join the DZone community and get the full member experience.
Join For FreeHave you implemented OpenHab Persistence and are looking to step up your rules? Using persistence on your rules can make them more powerful and efficient. In this post, I want to walk you through the different methods and how to use them.
This post is part of a series of three pieces on OpenHab Persistence:
- OpenHab Persistence Part 1: Restore The Value if your Items on Startup
- OpenHab Persistence Part 2: Build Graphs Using InfluxDB and Grafana
- OpenHab Persistence Part 3: Using Persistence in Your Rules
1. OpenHab Persistence in Rules: Why?
If you have reached this point, I think it is clear to you why implementing persistence is a game changer for your Home Automation Project. Now the question is...
How this can improve your rules?
The first reason is monitoring, being able to access prior data allows you to monitor and detect trends. This type of insight can be used to deliver alerts when needed.
Are you using your AC more than usual? Has the temperature of the fridge descended significantly in the last 2 hours?
Persistence is a great tool to deliver information within a context.
Another important feature is the ability to predict an event in the near future. For example, predicting when someone will be home based on presence detection over the past few weeks.
There is a practical example at the end of the post, in case you want to skip the theory.
2. OpenHab Persistence In Rules: Methods
On this section, I will walk you through the different methods that you can use in your rules. The persistence methods use JODA to specify the time. If you want to get into more complex features, you should check the JODA Docs.
<item>.persist
The first method to know about is persist, which is used to tell OpenHab that you want to store the value of an item right now.
mqtt_entrance_contact.persist
In this case, I am telling OpenHab to store the value of mqtt_entrance_contact in this very same moment.
The rest of the methods will be classified into three categories:
- Methods that detect if an item has been changed or updated within a period of time.
- Methods that collect metrics on the value of your items.
- The last category is reserved for those that retrieve historical information about your items.
Detect Whether an Item Has Changed or Updated
<item>.lastUpdate
Pretty simple, it will give the timestamp of the last update for <item>.
Example:
mqtt_entrance_contact.lastUpdate()
Response:
[INFO ] - lastUpdate: 2018-07-14T12:45:00.028-04:00
<item>.changedSince(AbstractInstant)
ChangeSince returns true if the item that you are requesting the data for has changed since AbstractInstant.
Example:
mqtt_entrance_contact.changedSince(now.minusHours(6))
Response:
[INFO ] - changedSince: false
This is telling you that the value of the item mqtt_entrance_contact has been the same one for the past 6 hours.
<item>.updatedSince(AbstractInstant)
updatedSince is similar to ChangeSince but it will report on every update that the item has gone through, regardless if that value has changed or not.
Example:
mqtt_entrance_contact.updatedSince(now.minusHours(6)))
Response:
[INFO ] - updatedSince: true
As you saw in the prior example, the value mqtt_entrance_contact has not changed in the last 6 hours, however, we used mqtt_entrance_contact.persist() at the beginning of the rule hence the item has just received an update.
Collect Metrics on Your Items
<item>.maximumSince(AbstractInstant)
Reference to the item with the maximum value since AbstractInstant.
Example:
mqtt_livingroom_temperature.maximumSince(now.minusHours(2)).state
Response:
[INFO ] - maximumSince: 34.0
Over the past two hours, the item mqtt_livingroom_temperature received a number of updates.
The method maximumSince()
will return a pointer to mqtt_livingroom_temperature when the temperature was at its highest value.
You might have noticed that in this case, I have used *.state at the end.
That is because this method returns a pointer to an object, as opposed to the ones that we have seen so far that return a value directly.
<item>.minimumSince(AbstractInstant)
Reference to the item with the minimum value since AbstractInstant.
Example:
mqtt_livingroom_temperature.minimumSince(now.minusHours(10)).state
Response:
[INFO ] - minimumSince: 18.2
<item>.averageSince(AbstractInstant)
Returns the average values for that item since AbstractInstant.
Example:
mqtt_livingroom_temperature.averageSince(now.minusMinutes(10))
Response:
[INFO ] - averageSince: 23.14730032883388
<item>.deltaSince(AbstractInstant)
DeltaSince will provide you with the value that a given item has been increased or decreased by.
Example:
mqtt_livingroom_temperature.deltaSince(now.minusMinutes(10))
Response:
[INFO ] - deltaSince: -0.9
The temperature in my living room has dropped by 0.9 degrees in the last 10 minutes.
<item>.sumSince(AbstractInstant)
Sum of all the different values since AbstractInstant.
Example:
mqtt_livingroom_temperature.sumSince(now.minusMinutes(10))
Response:
[INFO ] - sumSince: 324.1
Retrieve Historical Values
<item>.historicState(AbstractInstant)
Example:
mqtt_entrance_contact.historicState(parse("2018-07-10T16:42:00.0000")).state
Response:
[INFO ] - historicState: CLOSED
This method returns a pointer to the item mqtt_entrance_contact on July 10th at 4:42 PM.
<item>.previousState()
PreviousState provides a pointer to the item on its previous state.
Example:
mqtt_entrance_contact.previousState().state
Response:
[INFO ] - previousState: CLOSED
I haven't opened my entrance door in the past few hours so the immediate prior value is still CLOSED.
<item>.previousState(true)
When you pass true as a parameter to the previousState method, it returns the first previous state that is different from the current one.
Example:
mqtt_entrance_contact.previousState(true).state
Response:
[INFO ] - previousState(true): OPEN
3. OpenHab Persistence Rules: Practical Example
I want to finish this post with a practical example of a rule that I actually use myself.
I have several wireless nodes spread all over the apartment and I would like to monitor that they all sending updates in a timely manner.
Using persistence, this feature is very easy to implement.
As a prerequisite, I add all my nodes to OpenHab Groups (this is on the items file).
Contact mqtt_entrance_contact (gSensors,gSensorsContact) {mqtt="<[mosquitto:mygateway1-out/4/1/#:state:OPEN:1],<[mosquitto:mygateway1-out/4/1/#:state:CLOSED:0]"}
The rule is triggered by a cron job every 10 minutes to check if any of the nodes has gone on an IoT strike.
rule "Node Offline"
when
Time cron "0 0/10 * * * ?" // every 10 minutes
then
In the rule, I iterate over the different nodes in the group and check if they have been updated recently.
gSensorsContact.members.forEach
[ item |
if (!item.updatedSince(now.minusMinutes(120)))
{
sendNotification("test@gmail.com", "Node " + item.name + " is offline. Last Update: " + item.lastUpdate)
sendCommand(echo_livingroom_speak,"David, Node " + item.name + " appears to be offline.")
}
]
If the node hasn't been updated within the predefined interval, OpenHab will send me a notification on the cell phone and will broadcast a message through my Amazon Echo's around the apartment.
Here you have the full rule in case you want to check it out.
rule "Node Offline"
when
Time cron "0 0/10 * * * ?" // every 10 minutes
then
gSensorsTemperature.members.forEach
[ item |
if (!item.updatedSince(now.minusMinutes(10)))
{
sendNotification("test@gmail.com", "Node " + item.name + " is offline. Last Update: " + item.lastUpdate)
sendCommand(echo_livingroom_speak,"David, Node " + item.name + " appears to be offline.")
}
]
gSensorsHumidity.members.forEach
[ item |
if (!item.updatedSince(now.minusMinutes(10)))
{
sendNotification("test@gmail.com", "Node " + item.name + " is offline. Last Update: " + item.lastUpdate)
sendCommand(echo_livingroom_speak,"David, Node " + item.name + " appears to be offline.")
}
]
gSensorsContact.members.forEach
[ item |
if (!item.updatedSince(now.minusMinutes(120)))
{
sendNotification("test@gmail.com", "Node " + item.name + " is offline. Last Update: " + item.lastUpdate)
sendCommand(echo_livingroom_speak,"David, Node " + item.name + " appears to be offline.")
}
]
gSensorsMotion.members.forEach
[ item |
if (!item.updatedSince(now.minusMinutes(120)))
{
sendNotification("test@gmail.com", "Node " + item.name + " is offline. Last Update: " + item.lastUpdate)
sendCommand(echo_livingroom_speak,"David, Node " + item.name + " appears to be offline.")
}
]
end
As a side note, I use different groups because every node type has a different update interval. If you don't care about that, you could use the same group for all of them.
I hope you learned something today. If you have questions, leave them in the comments.
Published at DZone with permission of David Cabanero, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments