Using Home Assistant Companion App mobile data usage statistics to warn of high data use (using Node Red)

I wasn’t sure whether to post this in the Node Red subforum or the Home Assistant Companion App Subforum

I designed an automation in Node Red to give me a warning when mobile data usage is high.
This can be handy in some situations (e.g. if you’re abroad and you don’t realise you’re no longer on wifi, or sometimes I use a mobile hotspot so my son can play online games on his Nintendo Switch, but I don’t want him to download updates in such cases).

The Home Assistant Companion App enables two sensors showing mobile data usage:

  • Total data receives in GB since last reboot
  • Total data sent in GB since last reboot.
    I’ve combined the two into a single sensor which sums data sent and received by adding this to configuration.yaml:
template:
  - sensor:
    - name: "Mobile Quizzical mobile data consumed since reboot"
      unit_of_measurement: "GB"
      state_class: "total_increasing" #Enables long term statistics
      state: >
        {% set sent = states('sensor.mobile_quizzical_mobile_tx_gb') | float %}
        {% set received = states('sensor.mobile_quizzical_mobile_rx_gb') | float %}
        {{ (sent + received) }}

The documentation says the sensor gets updated every 15 minutes, but I notice this is very variable, e.g. in case of low data use the frequency seems lower:

Combined
Time Total GB
23:39:31 0.222
23:55:59 0.286
00:24:07 2.483
00:28:54 3.045
00:39:09 3.244
00:40:28 3.249
00:56:54 3.251
02:33:28 3.252
03:57:54 3.254
05:40:18 3.255
08:00:00 3.275
08:00:25 3.278
08:13:28 3.29
08:15:29 3.297
08:28:14 3.306
08:45:52 3.308
09:04:13 3.309

I’d want a warning if for example data usage reached 1 gigabyte and this in a timespan of less than one hour.

I’ve created an input helper in Home Assistant “input_number.warn_megabyte_per_hour_mobile_quizzical” so that in the UI I have a slider to dynamically adjust what should trigger a warning.

I’m using Node Red, and while I have made many dozens of automations I still don’t think I’m that good at it, but here’s what I came up with:

  • Whenever there’s new data coming in, I fetch the last hour of history. Each value is checked and only the last fully ascending series is kept (to account for the fact that if the smartphone was rebooted the data usage gets reset).
  • I check whether the difference between the lowest and highest value is larger than 1GB (in reality it’s comparing to a slider helper in Home Assistant so I can dynamically change the value for which I want to get a warning).

The main weakness is probably the limitation of how frequently it gets an update from the Home Assistant Companion App. Ideally the app would send an update very quickly when there is high data usage as it’s possible to use a lot of data in 15 minutes.

I’m sharing this as I haven’t seen anyone else who makes use of these sensors in the Home Assistant Companion App. And feel free to share a better version :slight_smile: There might be bugs as I haven’t done a full testing of all scenario’s.

[{"id":"4bd548ba89f5a12b","type":"api-get-history","z":"34843a6c.50d986","name":"Get 1 hour of history","server":"5c29d263.09d2ac","version":0,"startdate":"","enddate":"","entityid":"sensor.mobile_quizzical_mobile_data_consumed_since_reboot","entityidtype":"is","useRelativeTime":true,"relativeTime":"60 minutes","flatten":true,"output_type":"array","output_location_type":"msg","output_location":"payload","x":480,"y":280,"wires":[["7cf5d5c69508d79d","b4f3382a9032435b"]]},{"id":"2f6f6d7ab36e7fd6","type":"change","z":"34843a6c.50d986","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"\"Quizzical you've used \"&difference&\"GB of mobile data in less than 1 hour\"","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":1360,"y":280,"wires":[["90e2090f2265cbc1"]]},{"id":"90e2090f2265cbc1","type":"debug","z":"34843a6c.50d986","name":"debug 2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1520,"y":280,"wires":[]},{"id":"60b4bef1336b7fbb","type":"server-state-changed","z":"34843a6c.50d986","name":"Mobile quizzical","server":"5c29d263.09d2ac","version":4,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"sensor.mobile_quizzical_mobile_data_consumed_since_reboot","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":false,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":150,"y":280,"wires":[["10738e127024fb17"]]},{"id":"079391609e94d577","type":"switch","z":"34843a6c.50d986","name":"> warning limit?","property":"$globalContext(\"homeassistant.homeAssistant.states['input_number.warn_megabyte_per_hour_mobile_quizzical'].state\")","propertyType":"jsonata","rules":[{"t":"lte","v":"msg.differencemb","vt":"msg"}],"checkall":"true","repair":false,"outputs":1,"x":1180,"y":280,"wires":[["2f6f6d7ab36e7fd6","977e07e630b26f8d"]]},{"id":"b4f3382a9032435b","type":"function","z":"34843a6c.50d986","name":"Only keep last ascending series","func":"msg.payload = msg.payload.reduce((acc, obj) => {// iterates through \n          //payload array setting each element in turn to obj\n          // acc will be the output\n    let value = Number(obj.state); // changes obj.state to a number\n    if(value < acc.total){ // check to see if new state is lower than last\n        acc = {total:0,arr:[obj]}; // sets acc to object, wiping values\n    }else{// otherwise\n        acc.total = value;// sets acc.total to save last value\n        acc.arr.push(obj);// pushes obj to acc\n    }\n    return acc;// returns acc ready for next array element\n},{total:0,arr:[]}// initialises acc to an object with 2 properties\n).arr// output just the array from the object returned\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":730,"y":280,"wires":[["f16c8b60ae9f7587","1ca43fa4b2f30882"]]},{"id":"f16c8b60ae9f7587","type":"function","z":"34843a6c.50d986","name":"get Min & Max value","func":"const [min, max] = msg.payload.reduce((acc, current) => {\n    if(isNaN(current.state)) return [acc[0], acc[1]];\n    return [\n        Math.min(current.state, acc[0]), \n        Math.max(current.state, acc[1])\n    ];\n}, [Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY ]);\nmsg.difference = (max - min).toFixed(2);\nmsg.differencemb = (max - min) * 1000;\nmsg.payload = {min, max};\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":980,"y":280,"wires":[["079391609e94d577"]]},{"id":"10738e127024fb17","type":"trigger","z":"34843a6c.50d986","name":"wait 5s","op1":"","op2":"","op1type":"nul","op2type":"pay","duration":"5","extend":false,"overrideDelay":false,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":300,"y":280,"wires":[["4bd548ba89f5a12b"]]},{"id":"5c29d263.09d2ac","type":"server","name":"Home Assistant","version":4,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":false,"cacheJson":true,"heartbeat":false,"heartbeatInterval":30,"areaSelector":"friendlyName","deviceSelector":"friendlyName","entitySelector":"friendlyName","statusSeparator":"at: ","statusYear":"hidden","statusMonth":"short","statusDay":"numeric","statusHourCycle":"h23","statusTimeFormat":"h:m"}]

I end the automation here with the result. For you to see what you do with it (a simple message on your phone, a high priority message that sounds an alarm on your/your partners mobile device, automatically disconnect the mobile internet,…)

2 Likes

Sensors will only update if the state has changed since the last time it was checked for an update.

Also you can adjust the sensor update frequency to update once per minute if you want the checks to be faster. See the second paragraph from the link below.

2 Likes

Thanks!
What I do for the sensors that I build myself using ESPHome (and also most of the Z-Wave sensors I have support it) is to set a % change that triggers an update.
So it would report every X time, except if there is a large change then it updates immediately. This helps in terms of saving battery, network traffic etc.
But this is a nice to have, and I’m sure you have more exciting priorities for the excellent Companion App :slight_smile:

these sensors dont have an intent that gets fired when they get updated so they are poll based sensors. If you are unhappy with the sensor update frequency setting you can always update the sensors using a notification command to your own schedule :slight_smile:

1 Like

I see, thanks for all te advice. Battery life is important as well tough, so I’ll stick to the current approach rater than increasing the update frequency.
In any case the purpose of this post was mainly to share ad possibly improve, it was in no way intended as a complaint about the app or the sensors.

1 Like

At the risk of sounding slightly hypocritical when I promised not to complain, I wanted to set up the same solution on my wife’s mobile, but when I look at the values that the mobile data received sensor is giving me, it does not seem logical.
It should only go up, except in case of a reboot (which didn’t happen, and also then it would go to 0 not 0.038GB).
And my wife’s phone was on wifi for the whole period shown so it should not have moved (looking in the android settings for example it says that today it used 128KBbof mobile data).

Any clue what could be going on? It seems to be jumping between 0.8GB and 0.038GB multiple times.

The mobile data sent sensor is showing the same behaviour at the same moments in time but with different values.

As I understand it the ‘Total Rx GB’ is supposed to cover both mobile and wifi data. Therefore I was expecting see the sudden drops of the mobile data sensor in the total Rx GB sensor. But curiously enough that’s not the case.

The data you see here is data returned by the API, there are a few issues for it in Googles issue tracker but I believe each manufacturer has made changes to it as well.

On my Pixel 6 pro it increases as expected.

image

Thanks for the insights!
I conclude then that I’m my wife’s OnePlus Nord the sensor is not really useable - likely due to a strange implementation by OnePlus.

FYI I edited the first post because with great help over here the automation is improved and simplified.