Edit: Updates for OH 4
Please see Design Pattern: What is a Design Pattern and How Do I Use Them for how to read and use DPs.
Problem Statement
Often one will have a number of separate Items which are all related to each other in some way. For example, one might have a Contact
on a door and a DateTime
to represent when the last time the door was opened. This is easy enough to keep track of if there are only one or two such Items, but if one has a lot of similar Items or is using a lot of generically coded rules where one cannot hard code the names of the associated Items inside the rule mapping between the Items becomes difficult.
There are three approaches presented here to access an associated Item given another associated Item: Semantic Model, Group Membership, Similar Item Naming.
Semantic Model
If it makes sense, put the associated Items into the same Equipment in the semantic model. Then using the semantic model rule actions you can navigate the model to access the other Items that are in the same equipment. Once you have the Equipment, you can use the Item name, type, or tags to get at the associated Item.
For example, I add a “Status” Switch Item to many of my Equipment Groups to indicate whether or not a given Equipment is online/offline. I have a rule that detects when a sensor that should report periodically and stops reporting which calls another rule to when the sensor stops responding (see Threshold Alert and Open Reminder [4.0.0.0;4.9.9.9]). This called rule uses the Semantic Model to access the status Item given the sensor Item.
Blockly
As of this writing, the semantic actions are not available in Blockly. You can use the JS Scripting approach below using the “inline script” block or use one of the other approaches below.
JS Scripting
Once you have the parent Equipment, you can search it’s members or use other information contained in the Equipment Item to find the Item you are after.
To get the parent Equipment Item:
var equipment = actions.Semantics.getEquipment(items[event.itemName]);
Approach | Example |
---|---|
Equipment Name | items[equipment.name+'_Status']; |
Item Type | equipment.members.find( i => i.type = 'Switch' ); |
Item Tag | equipment.members.find( i => i.tags.includes('Status') ); |
Item Tags | equipment.members.find( i => ['Status', 'Power'].every(tag => i.tags.includes(tag) ); |
Multiple Criteria | equipment.members.find( i => i.tags.includes('Status') && i.name.includes('_Status') && i.type == 'Switch' ); |
In the example bleow I use the Item name to find the Status Item. The Status Item always follows the format of “Equipment_Item_Name_Status” so given the name of the Equipment Item I can create the name of the Status Item.
// Import alerting from my personal library, see the Separation of Behaviors DP
var {alerting} = require('rlk_personal');
var logger = log('Sensor Alert');
// Get the Equipment Item the sensor Item belongs to
var equipment = actions.Semantics.getEquipment(items[alertItem]);
// Get the status Item by appending "_Status" to the Equipment Item's name
var statusItem = items[equipment.name+'_Status'];
if(isAlerting && statusItem.state != 'OFF') {
statusItem.postUpdate('OFF');
alerting.sendAlert('Offline: ' + equipment.label + ' has stopped reporting', logger);
}
else if(!isAlerting && statusItem.state != 'ON') {
statusItem.postUpdate('ON');
alerting.sendAlert('Online: ' + equipment.label + ' is back', logger);
}
else {
console.info('Sensor status update alerting ' + isAlerting + ' initial ' + isInitialAlert + ' equipment ' + equipment.label + ' status ' + statusItem.state);
}
Group Membership
If you are not using the semantic model you can still use Group membership and various criteria to access an associated Item. This approach requires the rule to hard code the parent Group into the rule to search for.
Blockly
The above gets the member of the Group Sensors
whose name is the name of the triggering Item with “_Status” appended to it. Note there is no error checking for the case where such Item doesn’t exist. Replace the condition blocks plugged into the “if” to use other criteria (e.g. tags, metadata, etc.).
JS Scripting
Approach | Example |
---|---|
Item Name | items.Sensors.members.find(i => i.name = event.itemName + '_Status'); |
Item Tag | items.Sensors.members.find( i => i.tags.includes('Status') ); |
Item Tags | items.Sensors.members.find( i => ['Status', 'Power'].every(tag => i.tags.includes(tag) ); |
Multiple Criteria | items.Sensors.members.find( i => i.tags.includes('Status') && i.name.includes('_Status') && i.type == 'Switch' ); |
Item Names
Name Items that are associated with each other such that given the name of one Item, the names of the other Items can be created with just a little bit of String manipulation. With the associated Item’s name, one can get a reference to that Item by pulling it out of a Group’s members using the find
method described above (best for Rules DSL) or one can pull it out of the Item registry directly (best for all other languages).
Blockly
The “get substring” and “replace” blocks can be useful here as well.
JS Scripting
items[event.itemName+'_Status']
Jython
items[event.itemName+'_Status`]
events.postUpdate(event.itemName+'_Status')
Rules DSL
import org.eclipse.smarthome.model.script.ScriptServiceUtil // import access to the item registry
...
postUpdate(triggeringItemName+'_Status', ON)
val currState = ScriptServiceUtil.getItemRegistry.getItem(triggeringItemName+'_Status').state
Related Design Patterns
Design Pattern | How It’s Used |
---|---|
[Rules DSL] Get item from string name! | Source for the Item Registry Example |
Design Pattern: Unbound Item (aka Virtual Item) | Most associated Items are Unbound Items |
Design Pattern: Working with Groups in Rules | More examples of findFirst and forEach and other Group manipulations |
[Deprecated] Design Patterns: Generic Is Alive | The Group findFirst example is a specific implementation of this DP |
Design Pattern: Motion Sensor Timer | The anti-flapping timer in the Group findFirst example is a specific implementation of this DP |
Design Pattern: Human Readable Names in Messages | Used in the Group findFirst example to transform the Item name to a more human readable name for logs and alerts. |
Design Pattern: Separation of Behaviors | Alerting implementation in Group findFirst and the setting of vIsCloudy in the Item Registry example |
[Deprecated] Design Pattern: Time Of Day | Used to calculate the time of day in the Item Registry example |
Design Pattern: Using Item Metadata as an Alternative to Several DPs | Alternative approach using metadata. |
Edit: added JSR223 Python examples, minor grammar updates, created a picture.