Skip to main content

💧 aioflo: a Python3, asyncio-friendly library for Flo Smart Water Detectors

CI PyPi Version License Code Coverage Maintainability Say Thanks

Buy Me A Coffee

aioflo is a Python 3, asyncio-friendly library for interacting with Flo by Moen Smart Water Detectors.

Python Versions

aioflo is currently supported on:

  • Python 3.9
  • Python 3.10
  • Python 3.11

Installation

pip install aioflo

Usage

import asyncio
from datetime import datetime

from aiohttp import ClientSession

from aioflo import async_get_api


async def main() -> None:
    """Run!"""
    api = await async_get_api("<EMAIL>", "<PASSWORD>")

    # Get user account information:
    user_info = await api.user.get_info()
    a_location_id = user_info["locations"][0]["id"]

    # Get location (i.e., device) information:
    location_info = await api.location.get_info(a_location_id)

    # Get device information
    first_device = location_info["devices"][0]
    first_device_id = first_device["id"]
    device_info = await api.device.get_info(first_device_id)

    # Run a health test
    health_test_response = await api.device.run_health_test(first_device_id)

    # Close the shutoff valve
    close_valve_response = await api.device.close_valve(first_device_id)

    # Open the shutoff valve
    open_valve_response = await api.device.open_valve(first_device_id)

    # Get consumption info between a start and end datetime (location-wide aggregate):
    consumption_info = await api.water.get_consumption_info(
        a_location_id,
        datetime(2020, 1, 16, 0, 0),
        datetime(2020, 1, 16, 23, 59, 59, 999000),
    )

    # Scope consumption to a single device. Pass device_mac_address when a location
    # has multiple Flo devices; omit it for the location-wide total:
    device_consumption = await api.water.get_consumption_info(
        a_location_id,
        datetime(2020, 1, 16, 0, 0),
        datetime(2020, 1, 16, 23, 59, 59, 999000),
        device_mac_address=first_device["macAddress"],
    )

    # Get various other metrics related to water usage:
    metrics = await api.water.get_metrics(
        first_device["macAddress"],
        datetime(2020, 1, 16, 0, 0),
        datetime(2020, 1, 16, 23, 59, 59, 999000),
    )

    # Get recent Flo Detect water-flow events (near-real-time usage):
    events = await api.flodetect.get_events(
        first_device["macAddress"],
        to=datetime(2026, 7, 12, 10, 25, 4),
        limit=20,
    )

    # Set the device in "Away" mode:
    await set_mode_away(a_location_id)

    # Set the device in "Home" mode:
    await set_mode_home(a_location_id)

    # Set the device in "Sleep" mode for 120 minutes, then return to "Away" mode:
    await set_mode_sleep(a_location_id, 120, "away")


asyncio.run(main())

Moen SSO (Cognito) auth

The current Moen Smartwater app authenticates against Moen's SSO endpoint rather than the legacy Flo users/auth flow. use_sso=True opts into it: the access token is sent to api-gw.meetflo.com as a bearer token and is refreshed on expiry and on a 401, falling back to a full login if the refresh token is rejected.

api = await async_get_api("<EMAIL>", "<PASSWORD>", use_sso=True)

The legacy flow is the default and is unchanged. The legacy endpoint still works, so this is cover for it being retired rather than a fix for a current failure.

By default, the library creates a new connection to Flo with each coroutine. If you are calling a large number of coroutines (or merely want to squeeze out every second of runtime savings possible), an aiohttp ClientSession can be used for connection pooling:

import asyncio
from datetime import datetime

from aiohttp import ClientSession

from aioflo import async_get_api


async def main() -> None:
    """Create the aiohttp session and run the example."""
    async with ClientSession() as session:
        api = await async_get_api("<EMAIL>", "<PASSWORD>", session=session)

        # Tell Flo to get updated data from the device
        ping_response = await api.presence.ping()

        # Get user account information:
        user_info = await api.user.get_info()
        a_location_id = user_info["locations"][0]["id"]

        # Get location (i.e., device) information:
        location_info = await api.location.get_info(a_location_id)

        # Get device information
        first_device = location_info["devices"][0]
        first_device_id = first_device["id"]
        device_info = await api.device.get_info(first_device_id)

        # Run a health test
        health_test_response = await api.device.run_health_test(first_device_id)

        # Close the shutoff valve
        close_valve_response = await api.device.close_valve(first_device_id)

        # Open the shutoff valve
        open_valve_response = await api.device.open_valve(first_device_id)

        # Get consumption info between a start and end datetime (location-wide aggregate):
        consumption_info = await api.water.get_consumption_info(
            a_location_id,
            datetime(2020, 1, 16, 0, 0),
            datetime(2020, 1, 16, 23, 59, 59, 999000),
        )

        # Scope consumption to a single device. Pass device_mac_address when a location
        # has multiple Flo devices; omit it for the location-wide total:
        device_consumption = await api.water.get_consumption_info(
            a_location_id,
            datetime(2020, 1, 16, 0, 0),
            datetime(2020, 1, 16, 23, 59, 59, 999000),
            device_mac_address=first_device["macAddress"],
        )

        # Get various other metrics related to water usage:
        metrics = await api.water.get_metrics(
            first_device["macAddress"],
            datetime(2020, 1, 16, 0, 0),
            datetime(2020, 1, 16, 23, 59, 59, 999000),
        )

        # Get recent Flo Detect water-flow events (near-real-time usage):
        events = await api.flodetect.get_events(
            first_device["macAddress"],
            to=datetime(2026, 7, 12, 10, 25, 4),
            limit=20,
        )

        # Set the device in "Away" mode:
        await set_mode_away(a_location_id)

        # Set the device in "Home" mode:
        await set_mode_home(a_location_id)

        # Set the device in "Sleep" mode for 120 minutes, then return to "Away" mode:
        await set_mode_sleep(a_location_id, 120, "away")


asyncio.run(main())

Contributing

  1. Check for open features/bugs or initiate a discussion on one.
  2. Fork the repository.
  3. (optional, but highly recommended) Create a virtual environment: python3 -m venv .venv
  4. (optional, but highly recommended) Enter the virtual environment: source ./.venv/bin/activate
  5. Install the dev environment: script/setup
  6. Code your new feature or bug fix.
  7. Write tests that cover your new functionality.
  8. Run tests and ensure 100% code coverage: script/test
  9. Update README.md with any new documentation.
  10. Add yourself to AUTHORS.md.
  11. Submit a pull request!

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

aioflo-2026.9.1.tar.gz (11.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

aioflo-2026.9.1-py3-none-any.whl (13.9 kB view details)

Uploaded Python 3

File details

Details for the file aioflo-2026.9.1.tar.gz.

File metadata

  • Download URL: aioflo-2026.9.1.tar.gz
  • Upload date:
  • Size: 11.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.4.2 CPython/3.11.16 Linux/6.17.0-1022-azure

File hashes

Hashes for aioflo-2026.9.1.tar.gz
Algorithm Hash digest
SHA256 a2529ab313ff86685092465c15f415f75e8b7fffa06ae36d4f4c82327953c077
MD5 d6b0063f685e215301e3679110ff20f5
BLAKE2b-256 6ff114b96de50fc32ae9d66da974d0e6754202c86d522ac723c144ef3009670d

See more details on using hashes here.

File details

Details for the file aioflo-2026.9.1-py3-none-any.whl.

File metadata

  • Download URL: aioflo-2026.9.1-py3-none-any.whl
  • Upload date:
  • Size: 13.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.4.2 CPython/3.11.16 Linux/6.17.0-1022-azure

File hashes

Hashes for aioflo-2026.9.1-py3-none-any.whl
Algorithm Hash digest
SHA256 548e225cf0e69d37ceccacdd78846e34c2f912be5514df48ce9c4211ccffe47a
MD5 c9b8076bd213d0b8ef1b94d4bb869e61
BLAKE2b-256 19324e68bf4144d2a4d1f3431a31d7ea6469bdc39af74ed6d8adc760319750c0

See more details on using hashes here.

Release history Release notifications | RSS feed

2026.9.3

2 files

2026.9.2

2 files

This release

2026.9.1 This release

2 files

2026.9.0

2 files

2021.11.0

2 files

2021.10.0

2 files

0.4.3

2 files

0.4.2

2 files

0.4.1

2 files

0.4.0

2 files

0.3.0

2 files

0.2.0

2 files

0.1.5

2 files

0.1.4

2 files

0.1.0

2 files

0.0.1

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page