💧 aioflo: a Python3, asyncio-friendly library for Flo Smart Water Detectors
aioflo is a Python 3, asyncio-friendly library for interacting with
Flo by Moen Smart Water Detectors.
Installation
pip install aioflo
Usage
import asyncio
from datetime import datetime
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)
# Get all alarms:
alarms = await api.alarm.get_all()
# 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,
)
# The payload groups events per device under items[].events. Flatten it:
flow_events = api.flodetect.parse_events(events)
# Each event has startAt, endAt, totalGal, duration, and predicted.displayText.
# Set the device in "Away" mode:
await api.location.set_mode_away(a_location_id)
# Set the device in "Home" mode:
await api.location.set_mode_home(a_location_id)
# Set the device in "Sleep" mode for 120 minutes, then return to "Away" mode:
await api.location.set_mode_sleep(a_location_id, 120, "away")
asyncio.run(main())
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)
# Get all alarms:
alarms = await api.alarm.get_all()
# 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,
)
# The payload groups events per device under items[].events. Flatten it:
flow_events = api.flodetect.parse_events(events)
# Each event has startAt, endAt, totalGal, duration, and predicted.displayText.
# Set the device in "Away" mode:
await api.location.set_mode_away(a_location_id)
# Set the device in "Home" mode:
await api.location.set_mode_home(a_location_id)
# Set the device in "Sleep" mode for 120 minutes, then return to "Away" mode:
await api.location.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.
Release files for aioflo 2026.9.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| aioflo-2026.9.4.tar.gz | 12.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| aioflo-2026.9.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 27.3 kB
Release files / aioflo-2026.9.4.tar.gz
| Download URL | aioflo-2026.9.4.tar.gz |
|---|---|
| Size | 12.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fe1b18114fcce556d2e16e741091d8ff0af2087aecc499da0c9156f15beca005
|
|
BLAKE2b-256 checksum How to use checksums |
1234127dc1f6d802a2122bd8ad650624fbc1dc71c079c0fc515ec2ed52ab57f2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.5.1 CPython/3.13.15 Linux/6.17.0-1022-azure
|
Release files / aioflo-2026.9.4-py3-none-any.whl
| Download URL | aioflo-2026.9.4-py3-none-any.whl |
|---|---|
| Size | 15.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1953566ce315511e6b9d4a84e86c6f53550bbcb7b7c30cdf585641c6a51d7918
|
|
BLAKE2b-256 checksum How to use checksums |
cc339910125349d1f48b5c9797b308d51776b90282639bf87c91e511e866f9d1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.5.1 CPython/3.13.15 Linux/6.17.0-1022-azure
|