Calculating Water Consumption using Droplet (JS issue)

Running OH 5.1.1 on RPi5 configured entirely with Main UI

I recently installed a Droplet Water Sensor and integrated to OH with MQTT.

I’d like to have OH keep a running total of water used per day as well as display consumption events.

The vendor has a companion mobile app that does similar functions but I’d like to replicate this in OH.

There is an integration to Home Assistant but it’s not clear what functions it provides ( and I’m not well versed in HA)

A description of the HA integration is here 🤖 API setup - MQTT with Home Assistant | Droplet Resource Center

I imagine a strategy might be to capture the flow data with time stamps and use a rule to add to a running total.. I searched the community but didn’t find anything.

Thanks for any comments/ideas!

I can’t offer anything more detailed without more information. In particular what data are you getting be MQTT into OH and how often.

But, once you understand what the data it’s just a matter or keeping a running total every time the Item updates in another Item. Then at midnight transfer that total to another Item and reset it.

A similar rule for electricity can be found here: Historical energy consumption [4.0.0.0;5.9.9.9]

The whole “capture the flow data with time stamps” is handled by persistence. That’s literally it’s primary job. But we don’t need it for this. Though I bet you’ll want charts and graphs of this data so you will want to have your Items persisted. See Persistence | openHAB and Persistence | openHAB.

The device sends a few data items. I think we’re mainly interested in Flow and Volume.

Here’s what they look like in the event log.

19:52:49.988 [INFO ] [openhab.event.ItemStateUpdatedEvent  ] - Item 'WaterVolume' updated to 1138.77
19:52:49.989 [INFO ] [openhab.event.ItemStateChangedEvent  ] - Item 'WaterVolume' changed from 236.77 to 1138.77 (source: org.openhab.core.thing$mqtt:topic:63bc5ac12e:Droplet:Volume)
19:52:49.990 [INFO ] [openhab.event.ItemStateUpdatedEvent  ] - Item 'Droplet_Server' updated to Connected
19:52:49.990 [INFO ] [openhab.event.ItemStateUpdatedEvent  ] - Item 'Droplet_flow' updated to 4.08 l/min
19:52:49.991 [INFO ] [openhab.event.ItemStateChangedEvent  ] - Item 'Droplet_flow' changed from 3.95 l/min to 4.08 l/min (source: org.openhab.core.thing$mqtt:topic:63bc5ac12e:Droplet:flow)
19:52:51.572 [INFO ] [openhab.event.ItemStateUpdatedEvent  ] - Item 'Crawlspace_Temperature' updated to 5.1 °C
19:52:51.699 [INFO ] [openhab.event.ItemStateUpdatedEvent  ] - Item 'HVAC_Main' updated to 36 °C
19:52:57.118 [INFO ] [hab.event.ThingStatusInfoChangedEvent] - Thing 'shelly:shellyplus1:b8d61a8c3944' changed from OFFLINE (COMMUNICATION_ERROR): Unable to connect to device - NoRouteToHostException: No route to host to ONLINE (CONFIGURATION_PENDING): Device is initializing or in sleep mode.
19:53:00.188 [INFO ] [hab.event.ThingStatusInfoChangedEvent] - Thing 'shelly:shellyplus1:b8d61a8c3944' changed from ONLINE (CONFIGURATION_PENDING): Device is initializing or in sleep mode. to OFFLINE (COMMUNICATION_ERROR): Unable to connect to device - NoRouteToHostException: No route to host
19:53:03.744 [INFO ] [openhab.event.ItemStateUpdatedEvent  ] - Item 'ZWaveNode029ZW095HomeEnergyMeterGen5_Electricmeterwatts' updated to 4976.596
19:53:03.745 [INFO ] [openhab.event.ItemStateChangedEvent  ] - Item 'ZWaveNode029ZW095HomeEnergyMeterGen5_Electricmeterwatts' changed from 4958.63 to 4976.596 (source: org.openhab.core.thing$zwave:device:64793565f1:node29:meter_watts)
19:53:11.988 [INFO ] [openhab.event.ItemStateUpdatedEvent  ] - Item 'Gym_Temperature' updated to 18.4 °C
19:53:13.922 [INFO ] [openhab.event.ItemStateUpdatedEvent  ] - Item 'WaterVolume' updated to 1487.64
19:53:13.923 [INFO ] [openhab.event.ItemStateChangedEvent  ] - Item 'WaterVolume' changed from 1138.77 to 1487.64 (source: org.openhab.core.thing$mqtt:topic:63bc5ac12e:Droplet:Volume)
19:53:13.924 [INFO ] [openhab.event.ItemStateUpdatedEvent  ] - Item 'Droplet_Server' updated to Connected
19:53:13.924 [INFO ] [openhab.event.ItemStateUpdatedEvent  ] - Item 'Droplet_flow' updated to 0.75 l/min
19:53:13.925 [INFO ] [openhab.event.ItemStateChangedEvent  ] - Item 'Droplet_flow' changed from 4.08 l/min to 0.75 l/min (source: org.openhab.core.thing$mqtt:topic:63bc5ac12e:Droplet:flow)
19:53:18.017 [INFO ] [openhab.event.ItemStateUpdatedEvent  ] - Item 'WaterVolume' updated to -4.41
19:53:18.018 [INFO ] [openhab.event.ItemStateChangedEvent  ] - Item 'WaterVolume' changed from 1487.64 to -4.41 (source: org.openhab.core.thing$mqtt:topic:63bc5ac12e:Droplet:Volume)
19:53:18.018 [INFO ] [openhab.event.ItemStateUpdatedEvent  ] - Item 'Droplet_Server' updated to Connected
19:53:18.019 [INFO ] [openhab.event.ItemStateUpdatedEvent  ] - Item 'Droplet_flow' updated to 0 l/min
19:53:18.019 [INFO ] [openhab.event.ItemStateChangedEvent  ] - Item 'Droplet_flow' changed from 0.75 l/min to 0 l/min (source: org.openhab.core.thing$mqtt:topic:63bc5ac12e:Droplet:flow)

I do have RRD4J MapDB and Influx set up to persist on every change.

Here is the chart for Flow

And here’s what the flow data looks like in Grafana

I looked at this but don’t understand how to apply it to my use case. (Luckily my power consumption is handled by my watt meter.)

My understanding is I need a rule that triggers on every Flow update and calculates the elapsed time since the previous update (in seconds?) and use that duration multiplied by the flow (in L/min converted to L/sec ) to calculate the amount of water. Then add that amount to an Item that represents the running total.

Is that right?

There is also occasional Volume data but I have no idea what to do with that.

You don’t care about the flow rate, just the total amount of water used, right? And the device already reports the flow rate.

You just need a rule that triggers on changes to the WaterVolume Item and add the new value to the sum of the old values. You can create a separate Item for this (e.g. ‘DailyWaterUsage’). The code is two lines (JS).

var total = (items.DailyWaterUsage.isUninitialized) ? 0 : items.DailyWaterUsage.numericState;
items.DailyWaterUsage.postUpdate(total + parseFloat(event.newState));

Then you need a rule that runs once a day around midnight that adds the DailyWaterUsage to the MonthlyWaterUsage and resets the Daily Item to 0 and at the first of the month resets the MonthlyWaterUsage Item to 0. That’s what the Historical energy consumption rule template does.

If it were me, I’d configure the all these Items as Number:Volume and set the unit to L and set the MQTT Channel’s unit to L too. Then we can take advantage of UoM but the code changes to:

var total = (items.DailyWaterUsage.isUninitialized) ? Quantity('0 L): items.DailyWaterUsage.numericState;
items.DailyWaterUsage.postUpdate(total + Quantity(event.newState)); // the unit will already be attached to the state

If you want a running total you’d use persistence and you’d have to deal with timing and stuff like that. So the easiest would probably to set a cron triggered rule to run every couple of minutes with the one liner:

items.TwenyfourHourUsage.postUpdate(items.WaterUsage.persistence.sumSince(time.toZDT('P1D'), 'rrd4j'));

That will keep a running total of the usage over the past 24 hours instead of needing to be reset every day.

@rlkoshak
Thanks Rich you’re right, as usual. I was overthinking it.

I created an Item and Rule as suggested but clearly I’m missing something.

My rule is:

configuration: {}
triggers:
  - id: "1"
    configuration:
      itemName: WaterVolume
    type: core.ItemStateUpdateTrigger
conditions: []
actions:
  - inputs: {}
    id: "2"
    configuration:
      type: application/javascript
      script: >-
        var total = (items.DailyWaterUsage.isUninitialized) ? 0 :
        items.DailyWaterUsage.numericState;

        items.DailyWaterUsage.postUpdate(total + parseFloat(event.newState));
    type: script.ScriptAction

I set the WaterVolume item as volume in ml, because that’s what the sensor sends.
I set the DailyWaterUsage to L, thinking that the unit conversion happens automatically.

I get an error every time the rule runs.

09:25:58.130 [INFO ] [openhab.event.ItemStateUpdatedEvent  ] - Item 'WaterVolume' updated to 1437.97 ml
09:25:58.131 [INFO ] [openhab.event.ItemStateChangedEvent  ] - Item 'WaterVolume' changed from 477 ml to 1437.97 ml (source: org.openhab.core.thing$mqtt:topic:63bc5ac12e:Droplet:Volume)
09:25:58.131 [INFO ] [openhab.event.ItemStateUpdatedEvent  ] - Item 'Droplet_Server' updated to Connected
09:25:58.132 [INFO ] [openhab.event.ItemStateUpdatedEvent  ] - Item 'Droplet_flow' updated to 14.36 l/min
09:25:58.132 [ERROR] [l.handler.AbstractScriptModuleHandler] - Script execution of rule with UID 'WaterVolume' failed: TypeError: Cannot get property "isUninitialized" of null in <eval> at line number 3 at column number 13

Maybe because the rule doesn’t reference the WaterVolume?

You should set the channel on the MQTT thing’s unit to “ml”. Then you can set the unit on the Item to what every you want, even “gal”, and the ml will be converted automatically.

If everything is configured correctly it should. But it needs to be lower case “l”. I may have messed that up above but I just checked the docs and liter is “l” and not “L”.

No, the rule doesn’t have to reference that Item. All the information related to the update to the Item is in event. We just need the newState.

As for the error, double check the spelling exactly matches the Item you created to hold the running daily total. The error is saying items.DailyWaterUsage is null which means that Item doesn’t exist.

DailyWaterUsage is set l

version: 1
items:
  DailyWaterUsage:
    type: Number
    dimension: Volume
    label: DailyWaterUsage
    unit: l

WaterVolume is is set to ml

version: 1
items:
  WaterVolume:
    type: Number
    dimension: Volume
    label: Water Volume
    icon: water
    unit: ml
    groups:
      - gEnergy
    tags:
      - Energy
      - Measurement
    metadata:
      stateDescription:
        value: ' '
        config:
          pattern: '%.2f%'

Now that I’m using consistent Item names the error is

14:58:53.299 [INFO ] [openhab.event.ItemStateUpdatedEvent  ] - Item 'WaterVolume' updated to -31.91 ml
14:58:53.300 [INFO ] [openhab.event.ItemStateChangedEvent  ] - Item 'WaterVolume' changed from -27.43 ml to -31.91 ml (source: org.openhab.core.thing$mqtt:topic:63bc5ac12e:Droplet:Volume)
14:58:53.301 [INFO ] [openhab.event.ItemStateUpdatedEvent  ] - Item 'Droplet_Server' updated to Connected
14:58:53.300 [WARN ] [e.script.internal.action.BusEventImpl] - Cannot convert 'NaN' to a state type which item 'DailyWaterUsage' accepts: [DecimalType, QuantityType, UnDefType].
14:58:53.301 [INFO ] [openhab.event.ItemStateUpdatedEvent  ] - Item 'Droplet_flow' updated to 0 l/min

Set both Items to ml but getting same error

After several web searches I revised the rule to this

configuration: {}
triggers:
  - id: "1"
    configuration:
      itemName: WaterVolume
    type: core.ItemStateUpdateTrigger
conditions: []
actions:
  - inputs: {}
    id: "2"
    configuration:
      type: application/javascript
      script: >-
        var total = (items.DailyWaterUsage.isUninitialized) ? Quantity('0 ml'):
        items.DailyWaterUsage.quantityState;

        items.DailyWaterUsage.postUpdate(total + Quantity(event.newState)); //
        the unit will already be attached to the state
    type: script.ScriptAction

Now the error is

Script execution of rule with UID 'WaterVolume' failed: TypeError: TypeError: Argument of wrong type provided, required Item, string or Quantity.

My sensor Item is configured as Number:Volume
version: 1
items:
DailyWaterUsage:
type: Number
dimension: Volume
label: DailyWaterUsage
unit: ml```


and the DailyWaterUsage is also Number:Volume

version: 1
items:
DailyWaterUsage:
type: Number
dimension: Volume
label: DailyWaterUsage
unit: ml


The error points to TypeError.  Is my initialization statement incorrect?

Any help is appreciated!

I messed up the addition. You can’t use + with Quantity.

items.DailyWaterUsage.postUpdate(total.add(Quantity(event.newState)));

Using Gemini led me to a similar conclusion.

configuration: {}
triggers:
  - id: "1"
    configuration:
      itemName: WaterVolume
    type: core.ItemStateUpdateTrigger
conditions: []
actions:
  - inputs: {}
    id: "2"
    configuration:
      type: application/javascript
      script: >
        // Get the current total or default to 0 ml if uninitialized

        var total = (items.DailyWaterUsage.isUninitialized) ? Quantity('0 ml') :
        items.DailyWaterUsage.quantityState;


        // Wrap the new state in a Quantity and use the .add() method

        var increment = Quantity(event.newState);

        var newTotal = total.add(increment);


        // Post the update back to the item

        items.DailyWaterUsage.postUpdate(newTotal);
    type: script.ScriptAction

But still getting an error

 Script execution of rule with UID 'WaterVolume' failed: TypeError: TypeError: Argument of wrong type provided, required Item, string or Quantity.

It’s the event.newState maybe? Try calling event.newState.toString(). It should already be a String so :person_shrugging:

Tried that but more errors. Clearly don’t really know what I’m doing here.

Maybe time to try a different approach.

Could this be done with Blockly?

Sure, but Blockly “compiles” to JS. The code should look pretty much like this.

It’s unclear which line is failing.

When all else fails it pays to log out everything. And to review the docs. JavaScript Scripting - Automation | openHAB.

Looking at the rule trigger I now realize it’s an updated trigger, not a changed trigger like I expected. According to the docs for an updated trigger the state is event.receivedState, not event.newState. But logging out total, event.newState, increment, and newTotalwill confirm.

Just confirming changing that statement picking up the state solved the issue.
Thanks for your help. Working through this taught me a lot…

On dirait que vous êtes sur la bonne voie en pensant au MQTT et aux règles. Étant donné que votre capteur de gouttelettes publie des événements de flux ou de consommation via MQTT, vous pouvez capturer chaque message dans openHAB à l’aide d’un objet MQTT lié à un élément numérique représentant le flux actuel. Ensuite, avec une règle, vous pouvez ajouter la valeur à un élément persistant qui représente votre total quotidien. En utilisant le service de persistance d’openHAB, vous pouvez réinitialiser casino retrait immédiat https://trail-rando.fr/ ce total à minuit pour commencer une nouvelle journée. Pour afficher les événements de consommation, vous pouvez soit enregistrer chaque message dans un élément distinct, soit utiliser une liste déroulante dans un plan du site ou un widget d’interface utilisateur principal. Il s’agit essentiellement de combiner les abonnements MQTT, les règles d’accumulation et la persistance pour répliquer ce que fait l’application du fournisseur.