Atomic update to multiple items ...?

[OpenHAB 3, RULE-DSL]

Hi there, I am using the RULE DSL and I am hitting a brick wall.
Beeing used to parallel programming, I yearn to an atomic update or mechanism to update multiple items in one stroke, without having to dispatch many postUpdate/sendCommand s.

As it is, I am trying to implement an RTR with OpenHAB for a KNX based home.

The problem is: the RTR devices in each room have their own logic, so I try to side-channel my own logic onto the system.
Please note, still want to rely somewhat on the local logic in the RTR devices for redundancy.

In principle I have a rule, that is triggered when the mode for a room is changed (to comfort, to standby or to night).
This rule should look up the currently stored room temperature (different items) for that mode and set it accordingly.
If the temperature is changed, either within OpenHAB or at the local RTR devices, a different rule updates the stored temperature.

The problem: the change in room temperature involves setting the base temperature for that room, as well as modifying the temperature displacement. Each by updating an item.This fires off the temperature rule multiple times, messing up the stored temperatures.

Ideally, I would like to have the ability to update multiple items on bulk, and then fire it of as a single event.

Maybe, I am looking at this from the wrong angle, but I have already dumped a couple days worth of coding and exploring into his, and I am starting to get frustrated with the given interface.

Alternatively, I would love the have within a rule a fixed frame of reference, like a snapshot of all (marked) items at the time the rule was fired.

I also thought about using locks here, but this would only solve part of the quation, as the updates from the BUS and other commands would still be on the queue for processing.

Is there a more immediate interaface, where a programmer, experience in parallel programming could implement this?

Thanks for any input.

Frankly I didn’t get your point why you need to do something in parallel but you must not try to apply your parallel programming paradigms in openHAB.
openHAB isn’t made for realtime requirements, and at least rules DSL isn’t thread safe so drop this idea better sooner than later (or die trying get this to work reliably).
So yes better try switching angle. Your item updates don’t have to work in bulk if you set proper conditions when a rule is to apply. You can test for additional conditions early in a rule.

PS: what is RTR you must not expect us to know that.

Not possible.

That’s a different problem and it’s why the XY Problem causes us so much trouble on the forum. You don’t need to update the Items in a transaction, you need to prevent the rule from running for all the updates.

Post your rule and we should be able to help make it so it ignores the updates that it shouldn’t process or potentially change your overall approach to sidestep the real problem (i.e. make it so it doesn’t matter if your rule runs lots of times as multiple Items are updating). Trying to update multiple Items as an atomic operation is a dead end. It’s not possible and isn’t ever going to become possible.

A lock is also a dead end, as you surmise, because in OH 3 each individual rule already can only have one instance of itself running at a time and the events will queue up if they occur while the rule is already running. They are also really dangerous as some errors in Rules DSL (I don’t know about the other languages) don’t bubble back up to the rule so the finally in the try/catch/finally in your rule may never run and the lock never becoming unlocked.

You can only get this for the Item that triggered the rule in the event Object. It won’t preserve the other Item’s states. And what data gets preserved depends on the type of rule trigger.

But ultimately, as Marcus stated, this is a home automation system, not a real time system nor is OH architected to handle a use case like this.

Thank you for the quick responses. I will reply / update later this eavening.

RTR = room temperature regulator, own devices on the KNX bus. It has its own state of temperatures, and hvac modes.

As the others keep pointing out, it’s home automation. My take on that is that you are working with real-world devices. You cannot update all your KNX devices in one go; or at least you can’t get feedback from them in one instant. Meantime, a human can wander along and poke buttons at any time, while a sensor can send an autonomous report.
It’s an asynchronous world, openHAB makes a fair stab at dealing with that in its structure. You have to work with it, not against it.

Hello,
so let me try to explain.

Each room as a device, with buttons and a screen, that controls the rooms temperature.
The room temperature regulator has a couple of internal states.
A) A base temperature, usually the comfort temperature
B) an HVAC mode, i.e. comfort, stand-by, night, and frost-protection
C) a temperature offset that can be used locally to deviate from the temperatures programmed into the device

In principle it operates as follows:

var RTR_mode
var target_temperature
var offset  // in the range between -10° and + 10°
var base_temperature

switch (RTR_mode)
  case "Frost": target_temperature = 7° // or any other fixed temperature
  case "Comfort": target_temperature = base_temperature+offset
  case "Stand-By": target_temperature = base_temperature-2.0+offset
  case "Night": target_temperature = base_temperature-4+offset

You can change the mode locally, and you can change the offset locally. The offset resets after a mode-change.

My plan is to add in OpenHAB for every room items with the desired comfort temperature, stand-by temperature and night temperature.
Changes made on the device should reflect on these items, as well as changes on the items should reflect on the device, when the device switches to the corresponding mode.

My current plan was to have
a) a rule, that would trigger on changes in the mode, calculate the base_temperature and offset, and communicate that to the device.
b) a rule, that would trigger on changes in the target_temperature, read the current mode and store the new temperature to the corresponding items

The problem is then
a) when the “mode-rule” fires the new mode has already been set, so I can’t store the recent target temperature to the correct item in the mode rule. Therefore I need a rule to trigger on changes in target_temperature in order to store it to the correct mode-item.

b) when updating base_temperature and offset I need two seperate sendUpdate/sendCommand pairs, which in turn generate two updates on the target_temperature, which in turn fires that rule twice, messing up my temperature store in turn.

c) There is a race bound to happen, with these seperate entities.

As I want to preserve the ability to locally change the temperature on the device, I can’t deactive that function on the device.
Updating pairs of items via OpenHAB is not possible.

So, I am stuck.

Simplified rules, original rules are generic and use string transformations and pulls from object store to access the corresponding item, depending on the name of the triggering item. I hope,I did not mess things up, when simplifying things:

rule "RTR Target Temperature Change"
when
  Item TargetTemperatur changed
then
  switch (RoomMode.state) {
    case 1: RoomComfortTemperature.sendCommand(newState)
    case 2: RoomStandByTemperature.sendCommand(newState)
    case 3: RoomNightTemperature.sendCommand(newState)
  }
end
var Number auto_temperature = 20
var Number comfort_temperature = 21
var Number standby_temperature = 19
var Number night_temperature = 17
var Number RTRInternalOffset = -2
rule "RTR Mode Change"
when  
  Item RoomMode changed
then
var Number new_target_temperature
// pull target temperature from item
switch(newState) {
  case 0: new_target_temperature=auto_temperature
  case 1: new_target_temperature=RoomComfortTemperature.state
  case 2: new_target_temperature=RoomStandByTemperature.state - RTRInternalOffset
  case 3: new_target_temperature=RoomNightTemperature.state - 2*RTRInternalOffset
}
// transform "target temperature" to base_temperature and offset
var Number current_base = CurrentBaseTemperature.state
var Number new_offset = new_target_temperature - current_base
// map offset back to ]-10 .. 10 [
if ( new_offset >= 10.0 || new_offset <= -10.0)  {
  var Number new_base_temperature
  // must adjust base temperature to achieve target temperature
  if (new_offset < - 10.0) { // preserve some room for local adjustment
    new_base_temperature = (new_target_temperature + 10.0)
  } else if (new_offset > 10.0 ) {
    new_base_temperature = (new_target_temperature - 10.0)
  }
  new_offset = new_target_temperature - new_base_temperature
  BaseTemperature.sendCommand(new_base_temperature)
}
BaseTemperatureOffset.sendCommand(new_offset)
end

Since both rules interact with the same items and events can potentially occur concurrently, there is a problem.
Hence, I am stuck with the mechanisms in Rule-DSL with OpenHAB, or at least with my understanding thereof.

Well I don’t see your problem but then again frankly I don’t want think into your programming which I would have to if I wanted to debug your setup.
But that everyone has to do himself, we only provide you the direction and nudges it may take.
As I already said drop your programming paradigm first. Seems you haven’t done that yet because you still don’t want to fundamentally change your code. Change your thinking first or as I said die trying getting it to work. openHAB is event driven and asynchronous.

Note that in any rule, you can apply very specific triggers such as received command vs received update, received update X, changed from X to Y and more.
And you can add more actions at the beginning of a rule and “return” to exit rule processing.

The main Rule works, as desired.
The problem is, that when the main rule fires, the temperature update rule gets called multiple times as the intermediate stages of the temperaturechange occur.
This in turn causes the device on the bus to update the target_temperature multiple times, which in turn generates multiple events to OpenHAB.
The first event occurs, when the change is communicated due to the first postCommand(new_base_temperature) on the bus,
the scond event, when the second change is communicated due to the second postCommand(new_offset). After all, the target temperature is a device-local function of base_temperature and offset (and other internal states).

What I need are constraints on rules, such as an “AND”.
Using an event driven approach, I would then write:

rule "Update Temperature"
when 
Item TargetTemperatur changed AND
Item RoomMode  is 1

This would entirely eliminate the switch in the first rule.

The second rules inner workings is more a result of eliminating redundancy.
Naturally, I could write multiple ruls with change from x to y.

This would, for my setup of 10 rooms and 4 distinctive states, result in 120 rules with mostly identical bodys.

This is was I usually teach against - hardly maintainable.

Or am I missing s.th. here? I am willing to move more to an event or message driven approach, but at this point, I don’t see how.

Also of interest would be the ability to drop an event in an command rule.
I.e. that a rule could decide wether to commit a command, or reject it.

[Edited for typos]

You didn’t mention (or I missed it) which openHAB(2 or 3) you are using.
Because within openHAB 3 you can define in the ui a “conditional rule trigger” (see “But only if” in the screenshot)

So you would have the possibility to exectue a rule only under some circumstances (only if the temerature is not changed from the main rule).

On the other hand, you can set an item in your main rule that then prevents the other rule from performing the logic

if(mainRuleTriggerItem.state == ON)
  return;

Its an OpenHAB 3 - but I am using the rule-DSL, as I am currently having the feeling, that the UI does not scale to the complexity and/or the number of rules required.

Ps.: Thanks for all the input. Yearning to learn more.

Using that interface, a fragment of the functionality would likely look like

triggers:
  - id: "1"
    configuration:
      itemName: TargetTemperature
    type: core.ItemStateChangeTrigger
conditions:
  - id: "2"
    configuration:
      itemName: RoomMode
      operator: =
      state: "1"
    type: core.ItemStateCondition
actions:
  - id: "3"
    configuration:
      itemName: Room_ComfortTemperature
      state: TargetTemperature
    type: core.ItemStateUpdateAction```
rule "Update Temperature"
when 
    Item TargetTemperatur changed
then
    if (RoomMode.state != 1)
        return

    [...]

… getting there ?

I don’t think that’s your problem. It’s very easy to do that -

when 
   Item TargetTemperatur changed
then
   if (RoomMode.state == 1) { ...

Maybe you are trying to avoid that kind of use, viewing “unnecessary triggers” with horror or something? Don’t, it’s only HA not an efficiency test.

The overall impression I have is “so … what’s the problem?” I haven’t grasped why multiple trips round the temperature update cause an issue.

The problem is that there are basically 3 processes running concurrently:
a) The devices on the KNX-Bus, that updates the “targetTemperature” item via the input “base_temperature” and “offset”.
b) The Temperature rule, that updates the storage-items with the current temperature, in order to reset those stored temperatures in the 3rd rule
c) The mode-change rule, that changes base_temperature and offset, according to the stored items from b) and a room-mode change.

As I can’t sychronize all three items, the stored temperatures become corrupted, and in consequence, the whole temperature storage-retrival does not work.

Question: are all rules in a give “.rules” file mutexed, or is a single rule only mutexed?

Having thought a bit more, a contstraint such as
“after command x has completed, only then fire the rule.”.

S.th. similar to a std::future in C++

In Essence:
While I do some logic using the stored temperatures in the “RTR Mode Rule” the items can concurrently be modified.

Or to be a bit more sciency: the invariants that I plan with, are not preserved by the rules, due to overlap of rule 1 and 2.

No there is no mutexing or even thread safeness.

Again: stop thinking like a parallel programmer first.
It’s all asynchronous and event driven.

It’s still a logic problem, just a more complicated version of two stairway wallswitches working one light bulb. There you can get in loops, and the key to success is acting on changes not on updates.

So your KNX panels are doing an independent calculation of T using X and Y? And when they send T to openHAB the rules run? And when you work with OH UI, you calculate in rules and send new X,Y,T to panel? Then round it goes again …

The solution to that would be … don’t send T to the panel, use the panel’s version as “master” (the single lightbulb).

Agreed.
But, if I can not combine two events, then the amount of logic that can be expressed, is limited

No it isn’t. You’re showing a fundamental misunderstanding of what event driven means.
At any time there is only ever ONE event. In processing that, you can always check for states
(of your triggeringItem, other items, or global variables that you can set in any of the rules).

So, if I understand you correctly: OpenHAB is like a box with one screen. On that screen there is only one event at a time.
I am not allowed to recall what happend before the current screen, and I am not allowed to known what is next.
Hence, I need to make a decision on the current context of the screen.

However, I may query items, well knowing, that when I get the state of that item, other events might have occured & processed and ultimately changed the item from the time the event on the screen was visible.

Is that the gist of it?

The rule-trigger-subsystem is a box with one ear. It can only hear one event at a time because no matter powerful the box only one event can happen at a time, never in the same nanosecond.

Once triggered, rules take a finite time to execute, and the world continues to turn and so of course stuff may change. You can mitigate by taking a snapshot of important things, but even snapshots take time to actually do.