flowl — short for flower owl — /flaʊl/ like "fowl" but with an l
A small Rust service that exposes plant care data (watering schedules, care needs, etc.) for integration with Home Assistant and other automation platforms.
- Watering at a glance — see which plants need water and act with one tap
- Care journal — log watering, fertilizing, repotting, pruning, and custom events with optional photos
- AI plant identification — snap a photo, get the species and a full care profile
- AI care assistant — ask plant-specific questions in a chat, save the advice to your journal
- Home Assistant integration — each plant appears as an MQTT sensor with watering status
- Backup & restore — export and import all data and photos as a ZIP
- Works everywhere — responsive on phone, tablet, and desktop; installable as a PWA with offline support
- Light & dark theme — with English, German, and Spanish translations
- Single binary — self-contained Rust binary with embedded UI, just run it or use Docker
docker run -p 4100:4100 -v flowl-data:/data \
-e FLOWL_MQTT_DISABLED=true \
ghcr.io/simplyroba/flowl:latestOpen http://localhost:4100. Data is persisted in the flowl-data volume.
A docker-compose.yml is included in the repository. Uncomment the environment variables to enable all features.
docker compose up -d| Variable | Default | Description |
|---|---|---|
FLOWL_PORT |
4100 |
HTTP server listen port. |
FLOWL_DB_PATH |
/data/flowl.db |
Filesystem path to the SQLite database. |
FLOWL_LOG_LEVEL |
info |
tracing level filter for logs. |
FLOWL_MQTT_HOST |
localhost |
MQTT broker hostname. |
FLOWL_MQTT_PORT |
1883 |
MQTT broker port. |
FLOWL_MQTT_TOPIC_PREFIX |
flowl |
Topic prefix used for auto-discovery and plant topics. |
FLOWL_MQTT_DISABLED |
false |
Skip MQTT client, state checker, and publishes when set to true. |
FLOWL_AI_API_KEY |
— | API key for the OpenAI-compatible AI provider. AI features are disabled when unset. |
FLOWL_AI_BASE_URL |
https://api.openai.com/v1 |
Base URL for the AI API. |
FLOWL_AI_MODEL |
gpt-4.1-mini |
Model name used for all AI tasks. |
FLOWL_AI_RATE_LIMIT |
10 |
Max AI requests per minute (0 to disable). |
The model must support vision (image input), structured output (response_format: json_schema), and streaming (SSE).
| Model | Vision | Structured output | Streaming | Notes |
|---|---|---|---|---|
gpt-5-mini |
Yes | Yes | Yes | GPT-5 family, most capable but significantly more expensive |
gpt-4.1-mini |
Yes | Yes | Yes | Successor to gpt-4o-mini with better performance |
gpt-4.1-nano |
Yes | Yes | Yes | Cheapest OpenAI option, outperforms gpt-4o-mini |
gpt-4o-mini |
Yes | Yes | Yes | Previous generation, still capable |
| Model | Vision | Structured output | Streaming | Notes |
|---|---|---|---|---|
mistral-small-latest |
Yes | Yes | Yes | Best value — 24B, strong vision and structured output |
ministral-14b-latest |
Yes | Yes | Yes | 14B edge model, successor to pixtral-12b, Apache 2.0 |
To use Mistral, set FLOWL_AI_BASE_URL=https://api.mistral.ai/v1 and FLOWL_AI_API_KEY to your Mistral API key.
Any other provider exposing an OpenAI-compatible /v1/chat/completions endpoint (LM Studio, vLLM, Ollama with OpenAI shim, etc.) should also work as long as the model supports the capabilities above.
flowl works as an installable PWA and remains usable when your device can't reach the server.
What works offline:
- Browse the dashboard, plant details, and care journal using cached data from your last visit.
- View cached plant photos and thumbnails.
- Switch theme and language (stored locally).
What's disabled offline:
- Watering, adding log entries, editing or deleting plants.
- Creating new plants or editing existing ones.
- AI chat and plant identification.
- Data export and import.
An orange dot on the Settings icon indicates the app is offline. Pages that can't load any cached data show an offline message. When the server becomes reachable again, all controls re-enable automatically.
flowl has no built-in authentication. It is designed to run on a trusted home network or behind a reverse proxy that handles auth (e.g., Authelia, Authentik, Caddy with basic auth). Do not expose it directly to the internet.
With MQTT enabled, each plant appears as a sensor entity (sensor.flowl_<name>) via auto-discovery. The state is ok, due, or overdue. Attributes include last_watered, next_due, and watering_interval_days.
automation:
- alias: "Thirsty plants notification"
trigger:
- platform: time
at: "08:00:00"
condition:
- condition: template
value_template: >
{{ states.sensor
| selectattr('entity_id', 'match', 'sensor.flowl_')
| selectattr('state', 'in', ['due', 'overdue'])
| list | count > 0 }}
action:
- service: notify.mobile_app_your_phone
data:
title: "Plants need water"
message: >
{% set thirsty = states.sensor
| selectattr('entity_id', 'match', 'sensor.flowl_')
| selectattr('state', 'in', ['due', 'overdue'])
| list %}
{{ thirsty | count }} {{ 'plant needs' if thirsty | count == 1 else 'plants need' }} water:
{{ thirsty | map(attribute='name') | map('replace', 'flowl ', '') | join(', ') }}This project is developed spec-driven with AI assistance, reviewed by a critical human.




