Flume integration

Any chance you'd want to share that Flume/Node-RED integration work? I promise, I'll just use it as example code and won't request any support!

Yeah, let me get it together. it is pretty easy though - once you have your userId, locationId, and api login that is. My integration was never intended to be generic for other users, so I didn't bother extracting things like the userId and locationId automatically.

I'm working out the kinks on a Hubitat native (albeit still cloud reliant) integration, and I'll pick out the IDs automatically during setup. But I'm sure @JasonJoel's is great if you're already using node-red and don't mind the little bit of legwork.

Thanks guys, just having an example would be great. I'm in the process of moving all of my 42 Hubitat rules over to NR so I'm getting to know my way around that platform fairly well. Any examples, no matter how basic or hard-wired will be helpful. It's appreciated!

Here you go. This isn't the actual one I run, but a simplified flow with private info removed. It should give you an idea of how to get an oauth2 toke, refresh a token, and turn the Flume away mode on/off. There are other endpoints for fetching water use, etc, of course.

Edit the 2 oauth function nodes with your info. Edit the Set Location State and Get Location State node URLs and put in your clientid # and location #.

node red flow
[
    {
        "id": "c30e715.8ba869",
        "type": "tab",
        "label": "Flow 3",
        "disabled": false,
        "info": ""
    },
    {
        "id": "35055acc.f39056",
        "type": "http request",
        "z": "c30e715.8ba869",
        "name": "Get Tokens",
        "method": "POST",
        "ret": "obj",
        "paytoqs": "ignore",
        "url": "https://api.flumewater.com/oauth/token",
        "tls": "",
        "persist": false,
        "proxy": "",
        "authType": "",
        "x": 470,
        "y": 80,
        "wires": [
            [
                "5a06d574.6d262c",
                "9725c581.21d138",
                "30dfe80.8bf0118"
            ]
        ]
    },
    {
        "id": "ed82bfa9.80bb",
        "type": "function",
        "z": "c30e715.8ba869",
        "name": "oauth2Request",
        "func": "msg.headers='Content-Type: application/json'\n\nmsg.payload= {\n    \"grant_type\":\"password\",\n    \"client_id\":\"Enter_Client_ID_Here\",\n    \"client_secret\":\"Enter_Client_Secret_Here\",\n    \"username\":\"Enter_account_username_here\",\n    \"password\":\"Enter_account_password_here\"\n}\n\nreturn msg;\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 280,
        "y": 80,
        "wires": [
            [
                "35055acc.f39056"
            ]
        ]
    },
    {
        "id": "e678dae.b1a1d28",
        "type": "inject",
        "z": "c30e715.8ba869",
        "name": "",
        "props": [
            {
                "p": "payload",
                "v": "",
                "vt": "date"
            },
            {
                "p": "topic",
                "v": "",
                "vt": "string"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 100,
        "y": 80,
        "wires": [
            [
                "ed82bfa9.80bb"
            ]
        ]
    },
    {
        "id": "5a06d574.6d262c",
        "type": "debug",
        "z": "c30e715.8ba869",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 670,
        "y": 80,
        "wires": []
    },
    {
        "id": "63868470.6c669c",
        "type": "http request",
        "z": "c30e715.8ba869",
        "name": "Refresh Tokens",
        "method": "POST",
        "ret": "obj",
        "paytoqs": "ignore",
        "url": "https://api.flumewater.com/oauth/token",
        "tls": "",
        "persist": false,
        "proxy": "",
        "authType": "",
        "x": 480,
        "y": 220,
        "wires": [
            [
                "abef5ab6.d8d3c8",
                "9725c581.21d138",
                "30dfe80.8bf0118"
            ]
        ]
    },
    {
        "id": "c3b8c6c.6b47c38",
        "type": "function",
        "z": "c30e715.8ba869",
        "name": "oauth2Request",
        "func": "msg.headers='Content-Type: application/json'\n\nmsg.payload= {\n    \"grant_type\":\"password\",\n    \"client_id\":\"Enter_client_id_here\",\n    \"client_secret\":\"Enter_client_secret_here\",\n    \"username\":\"Enter_account_username_here\",\n    \"password\":\"Enter_account_password_here\"\n}\n\nreturn msg;\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 280,
        "y": 220,
        "wires": [
            [
                "63868470.6c669c"
            ]
        ]
    },
    {
        "id": "fc96cf26.5b4b2",
        "type": "inject",
        "z": "c30e715.8ba869",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "43200",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 110,
        "y": 220,
        "wires": [
            [
                "c3b8c6c.6b47c38"
            ]
        ]
    },
    {
        "id": "abef5ab6.d8d3c8",
        "type": "debug",
        "z": "c30e715.8ba869",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 670,
        "y": 220,
        "wires": []
    },
    {
        "id": "b83c3058.70473",
        "type": "http request",
        "z": "c30e715.8ba869",
        "name": "Get Location State",
        "method": "GET",
        "ret": "obj",
        "paytoqs": "ignore",
        "url": "https://api.flumewater.com/users/YOUR_CLIENTID/locations/YOUR_LOCATION",
        "tls": "",
        "persist": false,
        "proxy": "",
        "authType": "",
        "x": 490,
        "y": 580,
        "wires": [
            [
                "f0275e1b.3d8eb",
                "dcb23dac.e2f2a"
            ]
        ]
    },
    {
        "id": "77b4f79.8e3e808",
        "type": "function",
        "z": "c30e715.8ba869",
        "name": "oauth2Request",
        "func": "my_token = global.get(\"flume_access_token\");\n\n//node.warn(my_token);\n\nif (my_token) {\n    msg.headers = {\n        Authorization: \"Bearer \" + my_token\n    }\n\n    return msg;\n}",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 280,
        "y": 580,
        "wires": [
            [
                "b83c3058.70473",
                "f0275e1b.3d8eb"
            ]
        ]
    },
    {
        "id": "facab48b.205d58",
        "type": "inject",
        "z": "c30e715.8ba869",
        "name": "",
        "props": [
            {
                "p": "payload",
                "v": "",
                "vt": "date"
            },
            {
                "p": "topic",
                "v": "",
                "vt": "string"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 100,
        "y": 580,
        "wires": [
            [
                "77b4f79.8e3e808"
            ]
        ]
    },
    {
        "id": "f0275e1b.3d8eb",
        "type": "debug",
        "z": "c30e715.8ba869",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 670,
        "y": 540,
        "wires": []
    },
    {
        "id": "dcb23dac.e2f2a",
        "type": "change",
        "z": "c30e715.8ba869",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "flume_away_mode",
                "pt": "global",
                "to": "payload.data[0].away_mode",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 740,
        "y": 580,
        "wires": [
            []
        ]
    },
    {
        "id": "ef818986.3a7708",
        "type": "http request",
        "z": "c30e715.8ba869",
        "name": "Set Location State",
        "method": "use",
        "ret": "obj",
        "paytoqs": "ignore",
        "url": "https://api.flumewater.com/users/YOUR_CLIENTID/locations/YOUR_LOCATION",
        "tls": "",
        "persist": false,
        "proxy": "",
        "authType": "",
        "x": 490,
        "y": 420,
        "wires": [
            [
                "e93b7ba0.f2b8b8",
                "201a4847.db7e08"
            ]
        ]
    },
    {
        "id": "663350aa.9bc6f",
        "type": "function",
        "z": "c30e715.8ba869",
        "name": "false",
        "func": "my_token = global.get(\"flume_access_token\");\n\nif (my_token) {\n    msg.method = \"PATCH\";\n\n    msg.headers = {\n        \"Authorization\": \"Bearer \" + my_token,\n        \"Content-Type\": \"application/json\"\n    }\n\n    msg.payload = {\n        \"away_mode\": false\n    }\n\n    return msg;\n}",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 250,
        "y": 420,
        "wires": [
            [
                "ef818986.3a7708"
            ]
        ]
    },
    {
        "id": "5f65cdae.4f3ab4",
        "type": "inject",
        "z": "c30e715.8ba869",
        "name": "",
        "props": [
            {
                "p": "payload",
                "v": "",
                "vt": "date"
            },
            {
                "p": "topic",
                "v": "",
                "vt": "string"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 100,
        "y": 420,
        "wires": [
            [
                "663350aa.9bc6f"
            ]
        ]
    },
    {
        "id": "e93b7ba0.f2b8b8",
        "type": "debug",
        "z": "c30e715.8ba869",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 670,
        "y": 420,
        "wires": []
    },
    {
        "id": "a481ee26.19848",
        "type": "function",
        "z": "c30e715.8ba869",
        "name": "true",
        "func": "my_token = global.get(\"flume_access_token\");\n\nif (my_token) {\n    msg.method = \"PATCH\";\n\n    msg.headers = {\n        \"Authorization\": \"Bearer \" + my_token,\n        \"Content-Type\": \"application/json\"\n    }\n\n    msg.payload = {\"away_mode\": true}\n\n    return msg;\n}",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 250,
        "y": 480,
        "wires": [
            [
                "ef818986.3a7708"
            ]
        ]
    },
    {
        "id": "a7e66bd5.11cad8",
        "type": "inject",
        "z": "c30e715.8ba869",
        "name": "",
        "props": [
            {
                "p": "payload",
                "v": "",
                "vt": "date"
            },
            {
                "p": "topic",
                "v": "",
                "vt": "string"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 100,
        "y": 480,
        "wires": [
            [
                "a481ee26.19848"
            ]
        ]
    },
    {
        "id": "201a4847.db7e08",
        "type": "delay",
        "z": "c30e715.8ba869",
        "name": "",
        "pauseType": "delay",
        "timeout": "5",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "x": 680,
        "y": 460,
        "wires": [
            [
                "77b4f79.8e3e808"
            ]
        ]
    },
    {
        "id": "9725c581.21d138",
        "type": "change",
        "z": "c30e715.8ba869",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "flume_refresh_token",
                "pt": "global",
                "to": "payload.data[0].refresh_token",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 790,
        "y": 120,
        "wires": [
            []
        ]
    },
    {
        "id": "30dfe80.8bf0118",
        "type": "change",
        "z": "c30e715.8ba869",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "flume_access_token",
                "pt": "global",
                "to": "payload.data[0].access_token",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 790,
        "y": 180,
        "wires": [
            []
        ]
    }
]

I posted my Flume driver for Hubitat here: GitHub - tomwpublic/hubitat_flume

It is also listed on my HPM repo.

This version pulls usage data in two styles- one that matches the Flume app and one that is a straight lookback to make comparisons easier for different periods of time. See the readme for more info.

Thanks a ton to @jpalovick for a lot of testing and good suggestions for improvements.

7 Likes

FYI: @markb, @1a7dc3c08d03c361e0c5, @bruce4, @JoshBast, @YapFlapper, @pmusselman, @dtusia

Awesome work! Thank you @tomw

1 Like

Thank you gentlemen, most appreciated.

This evening my Hubitat seems to have died. It won't fully boot to a point where I can access it over http. As soon as I get that problem settled, I'll jump into this. But if I have to replace hub hardware I'm going to be wrestling z-wave devices for a few weeks. :weary:

I loaded the driver through HPM and it seems like all is good with it.

I had an Internet outage this morning for about 15 minutes and as I found out later of course Flume updates stopped with commStatus = error. I didn't look at it until about 3 hours later and it was still in the error state. Simply hitting Refresh on the device panel fixed it. Is there a way for the driver to automatically reconnect after an outage like that via a watchdog or is that a function of the cloud based interface that it may need to be refreshed after Internet outages. I'm most concerned because of the leak detection function which is best if it persistently runs. Thanks, Joe

I moved the next Refresh scheduling to after error handling for a failed Refresh. It was previously just not scheduled in the event of a failure. If your connection or their cloud goes down for a while it may introduce some additional unneeded traffic, but it is small and I agree this would be important to auto-recover.

I updated the version to 1.0.1, so an HPM Update should pick it up.

1 Like

Thank you very much for your time and effort in developing the Flume device driver for Hubitat.

In addition to the Flume water monitor, I have a Dome water main shut-off device. I would like to be able to use the Flume, when I am away from the house, to trigger the Dome device to turn off the water main if a high-flow event (e.g., water piper burst or joint failure) occurs. In the settings of my Flume interface, I have a "Custom Alert / High Flow Leak" set up to notify me (via push and email) if my water usage is 3 gal/min or more for 4 or more minutes.

Is there any way, via Rule Machine or Webcore, that I could set up the Dome device to turn off the water main if Flume detects a high flow leak?

Thank you for your help.

Do you have any way to simulate that high flow condition so that you can see what the Flume alerts look like?

I log every usage alert in an attribute called alertStream. There's also a Smart Leak detection that is sent through an alert, and when that comes through I send an event from the driver that sets the water attribute to wet. The latter is easy to handle in Rule Machine. The former is harder but still possible with a little bit of parsing of the JSON response.

What you'd need to figure out is whether your custom alert shows up in either or both of those to see what your options are.

I'm away from home for an extended period right now, but when I get home, I'll be able to trigger the high flow condition, and see how the custom alert shows up.

I'll post back here in this thread after I'm able to check this out.

Thanks much for your help and your speedy reply. Greatly appreciated.

1 Like

If he can't, I can. I get it when I fill my pool or run my sprinklers.

@tomw unless my sprinklers skip their cycle, should happen at a little after 730am tomorrow.

EDIT: Just happened @ 7:45am (sprinklers)

1 Like

Awesome. Did it toggle the water attribute on the device to wet?

Not sure what you mean by that. There really isn't any "wet" indicator that I know of.

All I see in the app is that you get a push notification / is listed in the notification list.

Sorry for the confusion. I thought you saw it happen in a device instance running my Flume Device driver. I was trying to see how it appears in the usage notifications. I implemented a WaterSensor capability that toggles when a Smart Leak alert is observed, so that is the "wet" indicator that I was talking about.

OH! Sorry, no.

@tomw

I just tried it again topping off my pool, this time with the driver installed.

Got the high flow notification in the Flume app, but got no events whatsoever on the Hubitat driver side.

Notification came in on the app at 6:42pm

1 Like