Rule On Member Update

I am trying to calibrate my appliance’s power consumption by trapping the power metrics the sensor sends periodically. I can’t figure out how to trigger a rule only once per received status update. My sensor sends one of the following MQTT messages:

MQT: tele/power_monitor/SENSOR = {"Time":"2018-09-11T22:54:28","ENERGY":{"Total":0.000,"Yesterday":0.000,"Today":0.000,"Period":0,"Power":0,"Factor":0.00,"Voltage":0,"Current":0.000}}
MQT: stat/power_monitor/STATUS10 = {"StatusSNS":{"Time":"2018-09-11T22:54:35","ENERGY":{"Total":0.000,"Yesterday":0.000,"Today":0.000,"Power":0,"Factor":0.00,"Voltage":0,"Current":0.000}}}

The SENSOR message ENERGY payload contains 8 metrics while the STATUS10 payload contains only 7 metrics (no Period).

My rule prints once for every member of the group (8 Items) for each metric in the message (64 for SENSOR, 56 for STATUS10). I want to log a set of metrics (8 or 7) only once per MQTT message. How can I avoid the repetition?

rule "Power Logging"
when
    Member of gPowerMonitor received update
then
    gPowerMonitor.members.forEach[o | 
        logInfo("power_log", "Power Logging - " + o.name + " = " + o.state.toString)
end

Items declarations:

Group:Switch:OR(OFF, ON) gPowerMonitor

Number      power_monitor_total
            "Power Monitor: Total [%.3f]" <none> (gPowerMonitor)
            { mqtt="
                    <[broker:tele/power_monitor/SENSOR:state:JSONPATH($.ENERGY.Total)],
                    <[broker:stat/power_monitor/STATUS10:state:JSONPATH($.StatusSNS.ENERGY.Total)]
                   " }
Number      power_monitor_yesterday
            "Power Monitor: Yesterday [%.3f]" <none> (gPowerMonitor)
            { mqtt="
                    <[broker:tele/power_monitor/SENSOR:state:JSONPATH($.ENERGY.Yesterday)],
                    <[broker:stat/power_monitor/STATUS10:state:JSONPATH($.StatusSNS.ENERGY.Yesterday)]
                   " }
Number      power_monitor_today
            "Power Monitor: Today [%.3f]" <none> (gPowerMonitor)
            { mqtt="
                    <[broker:tele/power_monitor/SENSOR:state:JSONPATH($.ENERGY.Today)],
                    <[broker:stat/power_monitor/STATUS10:state:JSONPATH($.StatusSNS.ENERGY.Today)]
                   " }
Number      power_monitor_period
            "Power Monitor: Period [%d]" <none> (gPowerMonitor)
            { mqtt="
                    <[broker:tele/power_monitor/SENSOR:state:JSONPATH($.ENERGY.Period)]
                   " }
Number      power_monitor_power
            "Power Monitor: Power [%d]" <none> (gPowerMonitor)
            { mqtt="
                    <[broker:tele/power_monitor/SENSOR:state:JSONPATH($.ENERGY.Power)],
                    <[broker:stat/power_monitor/STATUS10:state:JSONPATH($.StatusSNS.ENERGY.Power)]
                   " }
Number      power_monitor_powerFactor
            "Power Monitor: Power Factor [%.2f]" <none> (gPowerMonitor)
            { mqtt="
                    <[broker:tele/power_monitor/SENSOR:state:JSONPATH($.ENERGY.Factor)],
                    <[broker:stat/power_monitor/STATUS10:state:JSONPATH($.StatusSNS.ENERGY.Factor)]
                   " }
Number      power_monitor_voltage
            "Power Monitor: Voltage [%d]" <none> (gPowerMonitor)
            { mqtt="
                    <[broker:tele/power_monitor/SENSOR:state:JSONPATH($.ENERGY.Voltage)],
                    <[broker:stat/power_monitor/STATUS10:state:JSONPATH($.StatusSNS.ENERGY.Voltage)]
                   " }
Number      power_monitor_current
            "Power Monitor: Current [%.3f]" <none> (gPowerMonitor)
            { mqtt="
                    <[broker:tele/power_monitor/SENSOR:state:JSONPATH($.ENERGY.Current)],
                    <[broker:stat/power_monitor/STATUS10:state:JSONPATH($.StatusSNS.ENERGY.Current)]
                   " }

Thank you in advance for your advice.

Regards.

Mike

I’m not sure I understand what you are looking for. I think you’re looking to just have a single log entry, not one for very device in the group. If so, you can use something like this…

val message = "Warning! Low battery alert:\n\n" + gBattery.members.filter(item | item.state instanceof Number && item.state <= 50).sortBy[ (state as Number).intValue ].map[ label + ": " + state.format("%d%%") ].join(",\n")

This gives me…

Warning! Low battery alert:

Motion: Family Room Ceiling: 16%,
Motion: Living Room Ceiling: 16%,
Battery: Siren (Master Bedroom Downstairs): 34%,
Contact: Master Bathroom (upstairs): 50%

For example, for the SENSOR message…

The sensor sends an MQTT SENSOR message every 5 minutes. The message contains eight metrics.

I have eight Items declared. Each Item is subscribed to the MQTT SENSOR topic. The definition for each Item is set to parse out one of the eight metrics in the MQTT message JSON payload.

All eight Items belong to the gMonitorPower Group.

When an MQTT message is published, each Item is updated. This causes the Rule to be triggered for each member of the Group; 8 members - the Rule is invoked 8 times.

In the Rule, I loop through each member of the Group and log the current value of each Item. Thus I’m ending up with 64 log messages.

The STATUS10 message is similar except that it contains only seven metrics. The STATUS10 message is sent when a manual intervention with the sensor is performed. The message causes seven of the Items to be updated thus the Rule is invoked seven times. Again, within the rule, I am looping through each of the eight members of the Group. Thus I’m ending up with 56 log messages.

Preferably, I would want to know which member Item triggered the Rule and log the value for only that Item. Then, when the Rule runs eight (or seven) times, it will log one Item per invocation. What I can’t figure out is how to know programmatically in the Rule which Group member Item is the cause of the Rule being triggered.

Alternatively, I would want the Rule to only be triggered once per MQTT message and then I would loop through all member Items and log their values.

Regards.

Mike

OK, now I see what you’re after…

rule "Power Logging"
when
    Member of gPowerMonitor received update
then
    logInfo("power_log", "Power Logging - " + triggeringItem.name + " = " + triggeringItem.state.toString)
end

Though, I prefer not to use + for string concatenation…

logInfo("power_log", "Power Logging - {} = {}",triggeringItem.name,triggeringItem.state.toString)

If I understand him correctly he should try a single String Item which receives the whole MQTT state string (ie. SENSOR).
Then a rule that parses that MQTT string Item and updates every power_monitor_today/yesterday etc. Thus it’s a single rule that updates only once every 5 minutes (every mqtt update).

Edit: seems I misunderstood :wink:

triggeringItem - Bingo!

I KNEW it had to be simple…

Thanks!

Mike

@lfs_alp5

The reason I chose separate items for each metric is to let the JSONPATH do the parsing for me. I’ll scrape the log for my energy messages and pump that to my chart.

Cheers!

Mike