Design Pattern: Associated Items

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;5.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

image

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.openhab.core.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
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
Design Pattern: Simple State Machine (e.g. 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.

29 Likes
Lambda Procedure error: java.lang.NullPointerException
A More Clever System for "Locking" Lights?
Turning off devices based on Presence
Help with better HVAC rule design
How to make rules easy?
Receive fuel prices through dynamic items and save them
3 different methods to use scenes with Google Home & openHAB
Consolidation help
Cancel timer doesn`t work
Use an string item state as a name and state for another item
Suggestion for controlling my underfloor heating, Roth Touchline
Use variable for item
Cron Heating Rules
Using thing names variables to trigger other things with the same name
Alarm Notification "only" for the First OFF
Intergas Incomfort Lan2RF rules
Open the closed Rollershutter after Window opens
Magical light scenes
DP - Heating Boilerplate
Rule optimization: Window OPEN reminder
Programming question
Monoprice 6-zone Audio amp items, sitemap & rules
Simplifying Rule
Squeezebox Player WIP (Help Appreciated)
Array of type switch
Design Pattern: Working with Groups in Rules
Light switches
Variables vs. helper items
Make my rule files shorter --> working with groups
Iterating over a group, want to check an alternate item, sometimes
How to have a history of commands in Basic UI
Setting light themes in rules
Design Pattern: Gate Keeper
Taking A Rule To The Next Level
How to store additional info in ITEM?
Design Pattern: Encoding and Accessing Values in Rules
Type Conversions
Openhab.log goes crazy
Create a custom Item
Looking for optimal datastructure
JSR223 Jython Openhab Imports Erroring?
Roku Support
Rollertshutter item to Tuya Binding
Match name of similiar items in two different groups
Generating a dynamic variable name in a rule
New OH3 project
YAGSA - Yet Another Group Structure Approach
"First Time" Rule
Help with heating/time rule
Best practice for .rules
[SOLVED] Persistence query in habpanel
How to create a complex light status check rule?
reelyActive Smart Spaces Revisited
Basic switch questions, clarification about returned value
Design Pattern: Manual Trigger Detection
How to model location of an item?
Notification rule: how to format message?
Item or Variable Name with Variable as part of the name
Groups, and new JavaScript rules
Making complex Rules reusable or simpler
Coding Conventions in Rules
Design Pattern: Cascading Timers
Switch with 2 SendCommand actions
Rule not working anymore
JRule - openHAB Rules using Java
How to write rules for multiple/loop items
MAP: Invert Channel to Item works, but not Item to Channel: missing a bidirectional map?
MQTT long press design pattern (dimmer)
How to check if an item exists?
Alert when item in a group has not been updated for x hours?
Unable to get DateTime to work
Make rule with 196 if-statements more generic
JRule - openHAB Rules using Java
How can I build a dynamic Item name for itemValue?
Creating Capabilities with Rule Templates: Time of Day
Enable/Disable Alexa Voice Control
Help Needed: sendCommand not working with arrayList of String
Translate from javascript to rules DSL
Construct Other Item Names from another
Current and Correct Documentation of Syntax for Things and Items Needed
Lambda randomly fails with 'Script execution of rule with UID ... failed: null in
An Approach for Better Light Timers
Implementing a Script/Rule
Shutter control: "DOWN_BUT_TILT"
Automation/Orchestration Design Patterns
[SOLVED] 2 items in one line
Individual Alarm clock for each Day of week, with adjustable duration
InfluxDB as an alternative to mytimer.reschedule?
Use String variable in if statement to change Item state
Generic rule for update HVAC
Concatonating strings and variables in a rule
Member of using Prefix and replace
OH3 - How to get the type of an item
Whole house lighting
Arrays in Openhab 1.8.3
Groups within Rules
Range in case of Roller position
String type - cut a part of
Issue with working with Groups of Groups in Rules: Item not found when nesting groups
OpenHAB 2.0 Rules: Create list of HSBTypes
Return to earlier state
Send Modbus Data to File
OH 3 Tips and Tricks
Reusable Functions: A simple lambda example with copious notes
Handling with Groups
Exec binding: Controlling two bluetooth Eqiva EQ3 radiator valves simultaneously
Delay on item action exec binding
How to cancel/reset a createTimer in a generic rule
[Solved] JSON array? loop it in rule; rapid fire rule: concurrency?
Issues with implicit variables newState and triggeringItem in 3.0.0
Issues with implicit variables newState and triggeringItem in 3.0.0
[Solved] OH2: simplifying my rules...?
Is it possible to access the thing and location for an item in a rule?
Members of group item not recognized
[SOLVED] OH2: I am sure there is a better way to write this rule?
Global variables in OpenHAB and non-repetitive rules
Add a property to a item or type
Problem with manipulating a string for a rule
.members.filter DSL rule help request
Tutorial: How I make timers that show remaining execution time
Fetch another Item state, derived from a similar triggeringItem name
Weekly rotation through items in Sitemap
Optimization of rule
Design Pattern - Timer Management
CalDav usage and manually set events
Rollershutter up --> lights off
Patterns proposal: item states delay management and items dependencies (cascading)
Monitoring a lot of status informations
Hue bulbs and groups
Hue bulbs and groups
Help me optimize a simple rule (using items as variables, sending commands from an array)
Motion Sensor Advice
Converting group items into non-group items
How to set the light state back after blinking
How to store and use custom metadata for Items?
How to make a method / procedure from a rule
Heating and window contact - a (simple?) rule [SOLVED]
Struggles with stability
Item name as variable?
Is there a way to create "Animations" for devices?
[SOLVED] Rule retriggering Item state
Setup for home heating demand
Timer do not cancel properly
[solved] Item cast problem? Use string item to trigger Rollershutter item
How to implement 2-step motion sensor controlled lighting
Possibility to eliminate simple rules?
[SOLVED] Items definition in scripted automation
[SOLVED] Rule 'Extractor Fan sensor changed': null - another case
State of the item with combined name
[SOLVED] In sitemaps, how to create a view having item name and item value
Strange if/null behaviour
Help needed with groups
Magical light scenes
[SOLVED] Put rule code into method and call it in different rules
Same rule for different rooms
Replacing a sensor, what is best practice
Sending command to indirectly referred item possible?
Smart Virtual Thermostat (beta version)
Make use of Group based rules
Reuseable sitemap configuration
Yet another Heating Setup
Area Triggers --> Area Actions
Combine different rules and triggers into 1 rule
Finding an Item's Thing and locate a different Item in Rules
Device Offline Rule
Howto: Generate similar item/sitemap configurations using templates
Create reusable 'function' for roller-shutters
Tags vs. Metadata
(SOLVED) Uninitialized var
Group based power consumption
Rollershutter Group positioning
Item Naming
Item Naming
Automation #4: Smart Radiator (Generic Rule)
Journey to JSR223 Python 2 of 9
My Garadget + MQTT + OpenHAB Setup
[SOLVED] How do I debounce or antiflap notifications for many devices in a group?
triggeringItem problem (concurrency?)
Grafana Image Charts
How to calculate the time of a specific azimuth
Qubino Smart Meters (ZMNHXD, ZMNHTD) endless problems
Design Pattern: State Machine Driven Groups
Doing it smarter - help with rule code rollershutter automation
[SOLVED] Making this rule generic
Thermostat rules with manual override timeout
Help with forEach command to grab one item and loop to another Item
Sending multiple pictures with Telegram Action
KNX - Different DPT for rollershutter position set and read GA
No Global Variables? Time to use Node-Red
Arguments to createTimer
Create multiple Rules with similar configuration for different items
Grouping items in rules - customer data structures like pojo
[SOLVED] Dummy item pointing to real item?
Concat Strings and create itemName out of it
Help with rule to find out item state
Magical light scenes
[SOLVED] Using groups to shorten code
[SOLVED] Reversing a Switch
[SOLVED] Reversing a Switch
Heating weekly schedule - more efficient way to implement this
Naming convention
[SOLVED] Group Based Timestamps and durations with triggeringItem and Member of
How can I access generic items in a function?
On/off switch with 2 buttons
Design Pattern: DRY, How Not to Repeat Yourself in Rules DSL
Best way to create outbound mqtt message with json content?
Design Pattern: Working with Groups in Rules
Class simulation in rules
[SOLVED] Rollershutter group & rule
[SOLVED] Lambda calling other lambda? (JSR223/JYTHON)
Generic Presence Detection
Creating "mode switches" for seasonal activities
Finally not called -> deadlock
Roadmap to Happiness - What is missing in the core framework
Roadmap to Happiness - What is missing in the core framework
Hot Tank monitoring, with energy calculations
Getting rule name in rule
Basics of a Lambda expression
[SOLVED] Can't set NEST structure away in OH2.3
[SOLVED] Groupping items properly
Arrays as Global Variables
Displaying time since last update of an item
Using part of an item's name to reference another item in a rule
Combine number and rollershutter items into a single item for use with rule?
Lambda functions fail (not thread safe?)
Convert String to Item
Rule which iterates through group(s) of similar items
Rule which iterates through group(s) of similar items
Rule which iterates through group(s) of similar items
[Loxone Binding] Update of Openhab item, does not update Loxone item, Why?
Timers in functions not possible?
Groups ... if state in group1 execute action in group2
How does OH know the state of a switch?
Design Pattern : Expire Binding based Countdown timer
Http1 binding using a base url
Setting up a cron based sprinkler
Creating rules: how to make them general ? Shoudl I use proxy-items, or variables?
Rule to calculate pump filtration time depending on temperature
Help shortening one of my persistence rules
Feedback from a new openHAB(ian) user
Help with RPI Openhab full hang. (WAS Rule for longest ever uptime?)
Combining similar rules
Automatic lighting control without timers
Xtend Scripts vs JSR223?
Xtend Scripts vs JSR223?
Determine Object type in a Group (Dimmer or Switch)
Please test the new Expire Binding
[SOLVED] Question on the sendCommand()
Advice - New home, fresh install via Docker, fresh start
Item values and logical ranges at updates
[SOLVED] Openhab 2 Contact Sensor Time Rule
[SOLVED] Help me condense this rule
MQTT device initial state checking
Wildcard for Group.members.filter - Use String Patterns and ConfigItems
Fire Rule when groupmember receives command
Notifications in group design pattern
Time and temperature spreadsheet schedule
No working trigger functions openHAB 2.1
[SOLVED] Style Guide for openHAB?
Identify itemname based on part of a name
Set last tripped/alarm date, or last action date to an item - best practice?
Set last tripped/alarm date, or last action date to an item - best practice?
Handling devices that reset meters in an elegant way?
How I have automated my lights
Way to save system settings
Issue with final parameters when using lambda to create "function"
Operating hours counter
Rules triggering at Group-Change of an Item
Rules stop executing after a while
What are your top 3 automations
"event" is undefined in Script
Rule for Rollershutters - with proxy items and reed-contacts
Inactivity of items // no updates // automatically tracking
Lambda in rule fails with 'Error during the execution of rule '{RuleName}': null'
[SOLVED] Close Rollershutters when sun shines using groups
Generic rule to display last sensor update time
Can i pass items as arguments to rule?
Design Pattern: Manual Trigger Detection

Nice! I did not know that’s possible… or at least I didn’t know how. That would totally simplify one workaround I have in my setup. Thanks

I wasn’t aware of this item.lastUpdate, is that an OH2 only thing?

It’s been around as long as I’ve been using OH so at least since 1.6. It’s one of the standard persistence methods on all Items along with maxSince, minSince, previousState, etc. See the “Persistence Extensions in Scripts and Rules” section of the [Persistence wiki page](Persistence Extensions in Scripts and Rules).

If you don’t have persistence setup for the Item it returns null though.

Great - learning every day! Thanks @rlkoshak. Does it work with any persistence, i.e. MapDB, or just historical based stuff like InfluxDB?

It works for any queryable persistence so MapDB yes as well as InfuxDB et al but not with write only persistence services like MQTT or my.openhab.

I primarily use lastUpdate with MapDB myself.

Great stuff - thanks again, and thanks for your very useful tutorials on rule design patterns. I am sure many are gaining a lot of value from these.

1 Like

Thank you for your very helpful post about associated items!
I am now trying to create a generic rule that works for any room in the house but I have one problem:
I want to use a Timer in that rule. You showed how to get an Item by name that had been defined in an Items file before (ie DateTime). But I cannot define a Timer in an Items file nor is it possible to add a Timer to a group programmatically (?).
I do not want to use a single hard coded timer variable that is shared between all rooms because it might be the case that multiple timers are active at the same time.
Any suggestions how I could solve this?

That is correct.

Create a hashMap of Timers using the Item name as the key.

import java.util.Map

val Map<String, Timer> timers = newHashMap

rule "Rule that creates some Timers"
when
    Item MyGroup received update // or whatever
then
    val i = blah blah blah // what ever you do to get the Item

    val Timer t = timers.get(i.name)
    if(t == null) {
        timers.put(i.name, createTimer(now.plusMinutes(1), [|
            // do timer stuff
            timers.put(i.name, null)
        ])
    }
    else {
        timers.get(i.name).reschedule(now.plusMinutes(1))
    }
end
1 Like

I’m currently struggling with a rule. The rule needs to be triggered by any item in a specific group. So the trigger is similar as in your example.

However, in your example, the rule processes all items in that group. I would like my rule only to do stuff with the single item that was triggered.

So how do I identify the item (in group gDoors in your example) that triggered the rule?

Thanks,
Dries

[edit]
I may have found a solution, not sure if it is “best practice”, but initial tests seem to point out it is reliable.

when   
    Item gRaamcontact received update
then
	val LastWindowContact = gRaamcontact.members.sortBy[lastUpdate].last
	logInfo("Window","Last contact =" + LastWindowContact)
end

I’m not sure if it is always reliable when 2 contacts are changed at the same time…

[/edit]

It’s worth looking further in the Tutorials & Example forum section

Depending on the speed of your persistence you man need to add a sleep before the sortby.

It isn’t a best practice so much as the only way to do it in this case. The alternative is one rule per switch which each call a lambda.

Thank you both.

I thought I had read all the rule-tutorials by now, I guess I missed that one.

@rlkoshak: So far I didn’t had any persistence-issues. I guess my mapDB is fast enough. I just added a small sleep just to be sure (100ms). I didn’t want to make it too big, because then the chance of two contacts being changed at the same time will increase.

Hi.
I’ve been working through a few of the Design Pattern articles - they’re helping me get a better understanding of Openhab2. Thank-you for taking the time to write them up.

I have been trying to add the LastUpdate feature to my setup, using this as a guide. I think I’m nearly there, but I get an error at the assocDT.postUpdate(new DateTimeType(door.lastUpdate)) stage;
This gives the output (full detail further below)

2017-07-02 18:40:24.019 [ERROR] [.script.engine.ScriptExecutionThread] - Rule 'A Door's State Changed': Could not invoke constructor: org.eclipse.smarthome.core.library.types.DateTimeType.DateTimeType(java.lang.String)

The code I am using is very similar to the original post in this article (and the associated one on persistence), with a few log lines for debugging and a temporary workaround for Groups following a recent OH update: Groups seem to be broken - #30 by Kai

2017-07-02 18:32:47.040 [INFO ] [rthome.model.script.associated items] - Door state change rule started
2017-07-02 18:32:47.190 [INFO ] [rthome.model.script.associated items] - dtStr = testDoor_LastUpdate
2017-07-02 18:32:47.215 [INFO ] [rthome.model.script.associated items] - assocDT = testDoor_LastUpdate (Type=DateTimeItem, State=NULL, Label=test Door Last Update, Category=clock, Groups=[gDoorsLastUpdate])
2017-07-02 18:32:47.279 [INFO ] [rthome.model.script.associated items] - door.lastUpdate = 2017-07-02T18:32:46.000+01:00
2017-07-02 18:32:47.330 [ERROR] [.script.engine.ScriptExecutionThread] - Rule 'A Door's State Changed': Could not invoke constructor: org.eclipse.smarthome.core.library.types.DateTimeType.DateTimeType(java.lang.String)

Key snippets below - wondering if anyone can suggest how to fix?;

From .rules

//from: https://community.openhab.org/t/design-pattern-associated-items/15790

val logName = "associated items"

rule "A Door's State Changed"
when
    Item gDoors received update // NOTE: the rule will trigger multiple times per event
then
        logInfo(logName, "Door state change rule started")
        gDoors.members.forEach[door |
        // Get the associated DateTime Item
        val dtStr = door.name + "_LastUpdate"
        val assocDT = gDoorsLastUpdate.members.filter[dt|dt.name == dtStr].head as DateTimeItem
        logInfo(logName, "dtStr = " + dtStr)
        logInfo(logName, "assocDT = " + assocDT )
        logInfo(logName, "door.lastUpdate = "+ door.lastUpdate)

        // Update assocDT with the door's lastUpdate
        assocDT.postUpdate(new DateTimeType(door.lastUpdate))

    ]
end


from .items

//from: https://community.openhab.org/t/design-pattern-associated-items/15790


Group:Contact  gDoors                           // temporary workaround following recent OH update:https://community.openhab.org/t/groups-seem-to-be-broken/29307
Group gDoorsLastUpdate

Contact  testDoor               "test Door"                             <frontdoor>     (gDoors,GarageDoorGroup,gHistory,gNewDoorGroup)   {mqtt="<[mysensorsMQTT:mysensors/in/100/2/1/0/16:state:MAP(PIR.map)]"}
DateTime testDoor_LastUpdate    "test Door Last Update [%1$tm/%1$td %1tH:%1tM]" <clock> (gDoorsLastUpdate)

This gives the output:

2017-07-02 18:40:23.807 [INFO ] [rthome.model.script.associated items] - Door state change rule started
2017-07-02 18:40:23.952 [INFO ] [rthome.model.script.associated items] - dtStr = testDoor_LastUpdate
2017-07-02 18:40:23.970 [INFO ] [rthome.model.script.associated items] - assocDT = testDoor_LastUpdate (Type=DateTimeItem, State=NULL, Label=test Door Last Update, Category=clock, Groups=[gDoorsLastUpdate])
2017-07-02 18:40:23.996 [INFO ] [rthome.model.script.associated items] - door.lastUpdate = 2017-07-02T18:40:23.000+01:00
2017-07-02 18:40:24.019 [ERROR] [.script.engine.ScriptExecutionThread] - Rule 'A Door's State Changed': Could not invoke constructor: org.eclipse.smarthome.core.library.types.DateTimeType.DateTimeType(java.lang.String)

To be honest I understand enough of the syntax for the assocDT.postUpdate to work out what’s causing the error.

Any ideas on how to fix, or dig deeper into the logs?

Thanks

Luke.

lastUpdate returns a Joda DateTime object. You can not update a DateTimeItem with a Joda DateTime object. You either need to create a new DateTimeType using the last update.millis or you can try using door.lastUpdate.toString in your call to postUpdate. I think the default toString is the right format for OH to parse it into a DateTimeType.

1 Like

Thanks the .toString seems to be working

Recently I wondered what more goodies OH had in store to surprise me with.
I think you just showed one. This is totally useful and will apply on many things other than just door updates. Thanks

Great article.

Could you advise how your design proposal could work if I need to use pairs of objects. Like temperature measuring (room_temp) and target temperature (room_target_temp)? if I have 8 rooms, I would like to have a rule iterating every room and comparing corresponding room temperature with target temperature.

thanks.

Just like you describe. Just make sure you can name the Items in such a way that you can easily reconstruct the name of the associated Items using the name of the Item you are iterating over.

So, if you have a Bedroom_Temp name the associated Item Bedroom_Temp_Target and your loop would look something like:

Rooms.members.forEach[room |
    val target = TargetTemps.members.findFirst[room | room.name == room.name + "_Target"]
]

Thank you, Rich, makes perfect sense! as I understand in two loops the name variable “room” should not duplicate, so updated:

Rooms.members.forEach[room |
val target = TargetTemps.members.findFirst[r | room.name == r.name + “_Target”]
]