Rule with iterating through group members value as input, and output to another groups member items

I’m trying to get electricity price calculations for about 40 devices (and more to come surely) every 5 minutes. I have placed all price items, each representing a device, that show the cost of running the device for the past 5 minutes, these are all in a group - this is the output group.
My input group are the energy items that show the electricity usage of the devices - this is the input group.

For testing I have made a rule for a single device, that is run every 5 min:

var energy = <item_in_input_group_showing_kWh>.deltaSince(now.minusMinutes(5), "influxdb") as Number
var Number price = energy * (<current_electricity_price_pr_kWh>.state as Number)
<item_in_output_group_showing_cost_for_last_5min>.sendCommand(price)

Rather than making 40+ rules, or 1 rule with 40+ different iterations of the above, with different items, and adding to that rule for each new device, I’d like to know if there is a way to simply add an item to the input group measuring kWh, and adding a cost-of-device-item to the output group, and the rule iterates over all members of the input group, multiplying the kWh consumption by the current electricity price, and outputting the value to an item in the output group.
My struggle is how do I efficiently index, or ‘link’ a calculation item to the kWh item, so the pairing of energy-usage-of-device-X will be output to the cost item of said device X.

I’d prefer using DSL as a language, but its more important to me to have a working rule, than a specific language.
If it makes any difference the OH instance is 3.4.5, running on an Openhabian RPi.

There are several ways of doing this. The simplest is by using a consistent naming for the two types of items. For example

Energy_XXX <=> Cost_XXX

Code:

EnergyGroup.members.forEach [ energyItem |
  val name = energyItem.name.split("_", 2)[1]
  val energy = energyItem.deltaSince(now.minusMinutes(5), "influxdb") as Number
  val price = energy * (ElectricityPrice.state as Number)
  postUpdate("Cost_" + name, price)
]

PS I haven’t used rulesdsl in a while so the above code may have some errors. Untested.

Hi Jim
Thanks that could work, but since I already have named my Energy items, is it possible to use the item labels somehow instead, since I can change those?

Yes it’s possible but I wouldn’t do that. It is too obscure to rely on the Label because 6 months later you might have forgotten that the label is important and you’d go and change it without realising that it would break something.

You could use metadata instead. The strategy is to assign the same unique metadata value for each “device” to both the energy and cost items. e.g. assign LivingRoomTV to both LivingRoomTV_Energy and LivingRoomTV_Cost.

Then for each energyitem, get its metadata value, you would iterate through the CostItems group looking for an item that has the same metadata value.

The problem is I don’t think RulesDSL can deal with metadata, or at least I’m not sure that it could.

You could use tags, and tag it something like “Cost_XXX” (the Cost_ part would be fixed and the same for both the energy and cost items) and look it up that way.

Another approach involving the use of semantic tags, but rulesdsl AFAIK also can’t deal with it.

I can give you concrete examples in jruby because that’s the language I’m using and most familiar with.

Another way is to create the mapping in your rule file, and it’s a simple matter of looking up the mapping that way. Once again I’m very unproficient in rulesdsl to give you a concrete example, but I’ll try

val costMap = newHashMap(
  LivingRoomTV_Energy_Item -> LivingRoomTV_Cost_Item,
  ...
)

EnergyGroup.members.forEach [ energyItem |
  val costItem = costMap.get(energyItem)
  if (costItem != null) {
    val energy = energyItem.deltaSince(now.minusMinutes(5), "influxdb") as Number
    val price = energy * (ElectricityPrice.state as Number)
    costItem.postUpdate(price)
  } else {
    logWarn("Cost Calculation", "Cannot find the cost item for {}", energyItem.name)
  }
]

Thanks for the explenation, and even though I have no intention of changing the labels, I know you are right about intentions and future prospects :laughing:

I’ll just create new cost items to stick to a naming convention - that would work best in the long run, and using tags or metadata would give me more work as that requires again, going into each items and putting extra data in manually, also in the future.

Thanks for you help :beers:

I just want to confirm that it cannot.

In this case it can. The semantic actions can be used in Rules DSL. I personally have become more a fan of this approach, where it makes sense.

just for the sake of completion, your dsl example needs a simple little tweak:

postUpdate("Cost_" + name, price.toString)

Other than that I get exactly what I want, I now can simply add the items to my energy group, and make a price item, and they automagically calculate the price for me :grin:

Since my energy items already have their name from before I just created the new cost items with the same names as my energy items, but with a “Cost” -prefix, like you suggested. This way there is no need to split the item names, I just take the full name and prepend the “Cost_” like in your example.

Great success! Thanks again for your help :vulcan_salute:

1 Like