Water Meter
Mon installation personnelle pour suivre la consommation d’eau dans Home Assistant à partir d’un compteur analogique, avec une caméra ESP32, ESPHome, Docker et une lecture d’image basée sur de l’IA.
Introduction
Au départ, j’utilisais le projet AI-on-the-edge-device. Il fonctionnait bien et m’a permis de valider l’idée assez rapidement.
Avec le temps, la fiabilité est devenue plus délicate chez moi: dès que la caméra bougeait un peu, même légèrement, la lecture pouvait devenir moins stable. J’ai donc créé ma propre approche basée sur de l’IA, avec l’objectif d’être plus tolérant aux petits décalages et plus fiable dans mon installation.
Pré-requis
Voici le matériel et les services que j’utilise. L’installation MQTT n’est pas détaillée ici: je pars du principe qu’un serveur MQTT est déjà disponible et configuré.
- Une carte caméra ESP32.
- Un module caméra OV2640.
- Important: le module OV3660 n’est pas compatible.
- Home Assistant.
- Un environnement Docker / Docker Compose.
- Un serveur MQTT.
- Optionnel: une imprimante 3D pour le support de montage.
Pour fixer la caméra devant le compteur, j’utilise ce support imprimé en 3D: Watermeter Camera Mount sur Thingiverse.
Configuration ESPHome
L’ESP32 est flashé avec ESPHome. Une fois configuré, le flux de la caméra et le flash deviennent
disponibles dans Home Assistant. Dans mon cas, l’entité de flash ressemble à
light.watermeter_watermeter_flash.
Cette entité est utilisée plus tard par l’application WaterMeter pour éclairer le compteur avant de
prendre une capture. Voici la configuration ESPHome utilisée pour exposer la caméra en snapshot
sur le port 8080 et piloter le flash.
esphome:
name: esphome-web-a80724
friendly_name: Watermeter
name_add_mac_suffix: false
esp32:
board: esp32cam
framework:
type: arduino
psram:
mode: quad
speed: 40MHz
logger:
api:
ota:
platform: esphome
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
esp32_camera:
name: Watermeter Cam
external_clock:
pin: GPIO0
frequency: 20MHz
i2c_pins:
sda: GPIO26
scl: GPIO27
data_pins: [GPIO5, GPIO18, GPIO19, GPIO21, GPIO36, GPIO39, GPIO34, GPIO35]
vsync_pin: GPIO25
href_pin: GPIO23
pixel_clock_pin: GPIO22
power_down_pin: GPIO32
resolution: 640x480
jpeg_quality: 12
esp32_camera_web_server:
- port: 8080
mode: snapshot
output:
- platform: gpio
pin: GPIO4
id: cam_flash
light:
- platform: binary
name: "Watermeter Flash"
output: cam_flash
Conteneurs Docker
L’installation utilise deux conteneurs: WaterMeter, qui orchestre les captures, Home Assistant et MQTT, et DigitVisionAI, qui lit les chiffres sur l’image.
services:
digit-vision-ai:
image: ghcr.io/jerome-marquis/digitvisionai:latest
container_name: digit-vision-ai
ports:
- "8000:8000"
volumes:
- ./models:/app/models:ro
environment:
YOLO_WINDOW_MODEL: /app/models/digit-vision-ai-roi-2026010401.pt
YOLO_DIGITS_MODEL: /app/models/digit-vision-ai-crop-digits-2026051601.pt
CONF_WINDOW: "0.5"
CONF_DIGITS: "0.5"
IOU: "0.3"
EXPECTED_DIGITS: "7"
CLUSTER_GAP_RATIO: "0.6"
labels:
- "com.centurylinklabs.watchtower.enable=true"
restart: unless-stopped
watermeter:
image: ghcr.io/jerome-marquis/watermeter:latest
container_name: watermeter
depends_on:
- digit-vision-ai
volumes:
- ./data:/data
environment:
INITIAL_WATER_LITERS: "<INITIAL_COUNTER_VALUE_IN_LITERS>"
DISCOVERY_INTERVAL_SECONDS: "600"
INTERVAL_DAY_SECONDS: "60"
INTERVAL_NIGHT_SECONDS: "1800"
NIGHT_START_HOUR: "23"
NIGHT_END_HOUR: "6"
MAX_DELTA_DAY_L: "50"
MAX_DELTA_NIGHT_L: "100"
OPENAI_EXPECTED_DIGITS: "7"
HA_URL: "http://<HOME_ASSISTANT_HOST>:8123"
HA_TOKEN: "<HOME_ASSISTANT_LONG_LIVED_ACCESS_TOKEN>"
FLASH_ENTITY: "light.watermeter_watermeter_flash"
SNAPSHOT_URL: "http://<ESP32_CAMERA_HOST>:8080/"
FLASH_PRE_SECONDS: "3"
FLASH_POST_SECONDS: "3"
SNAPSHOT_RETRY_COUNT: "5"
SNAPSHOT_MIN_BRIGHTNESS: "25"
MAX_IMAGES: "500"
MQTT_HOST: "<MQTT_HOST>"
MQTT_PORT: "1883"
MQTT_USER: "<MQTT_USER>"
MQTT_PASS: "<MQTT_PASSWORD>"
MQTT_TOPIC: "watermeter/total_l"
MQTT_RETAIN: "true"
HA_DISCOVERY: "true"
HA_DISCOVERY_PREFIX: "homeassistant"
HA_SENSOR_NAME: "Water Meter Total (AI)"
HA_SENSOR_ID: "watermeter_total_l_ai"
AI_PROVIDER: "local"
LOCAL_AI_ENDPOINT: "http://digit-vision-ai:8000/read"
LOCAL_AI_TIMEOUT: "15"
AI_ENDPOINT: "https://api.openai.com/v1/responses"
AI_API_KEY: "<OPTIONAL_AI_API_KEY>"
AI_MODEL: "gpt-4.1-mini"
AI_TIMEOUT: "30"
AI_RETRIES: "2"
labels:
- "com.centurylinklabs.watchtower.enable=true"
restart: unless-stopped
Téléchargement des modèles IA
DigitVisionAI a besoin de deux modèles pour détecter la zone du compteur puis lire les chiffres.
Dans cette installation, ils sont généralement placés dans le dossier ./models
monté dans le conteneur Docker.
digit-vision-ai-roi-2026010401.pt
Modèle utilisé pour détecter la zone de lecture du compteur.
digit-vision-ai-crop-digits-2026051601.pt
Modèle utilisé pour lire les chiffres dans la zone détectée.
Variables importantes
Le fichier Docker Compose peut contenir beaucoup de réglages. Voici uniquement les variables que je considère comme les plus importantes pour comprendre l’installation.
DigitVisionAI
- YOLO_WINDOW_MODEL
- Modèle utilisé pour détecter la zone utile du compteur.
- YOLO_DIGITS_MODEL
- Modèle utilisé pour lire les chiffres dans la zone détectée.
Les modèles utilisés dans mon installation sont
digit-vision-ai-roi-2026010401.pt et
digit-vision-ai-crop-digits-2026051601.pt.
Ils doivent être placés dans un dossier de modèles approprié, par exemple
./models monté dans le conteneur.
WaterMeter
- HA_URL
- URL de votre instance Home Assistant.
- HA_TOKEN
- Token longue durée Home Assistant. À garder privé.
- FLASH_ENTITY
- Entité du flash ESPHome, par exemple
light.watermeter_watermeter_flash. - SNAPSHOT_URL
- URL du flux/cliché de la caméra ESP32. Dans mon cas, elle passe généralement par le port
:8080.
MQTT
- MQTT_HOST
- Adresse du serveur MQTT.
- MQTT_PORT
- Port MQTT, souvent
1883. - MQTT_USER / MQTT_PASS
- Identifiants MQTT. À remplacer par vos propres valeurs.
- MQTT_TOPIC
- Topic utilisé pour publier la valeur totale.
- MQTT_RETAIN
- Indique si MQTT conserve la dernière valeur publiée.
IA optionnelle
Une IA externe peut servir de solution de secours si la lecture locale rencontre un problème.
- AI_ENDPOINT
- URL/endpoint de l’IA externe.
- AI_API_KEY
- Clé API optionnelle. À garder privée.
- AI_MODEL
- Modèle utilisé par l’API IA externe.
Intégration Home Assistant
Une fois les conteneurs démarrés, le capteur devrait apparaître automatiquement dans Home Assistant via la découverte MQTT. Il peut ensuite être ajouté au tableau de bord Énergie pour suivre la consommation d’eau.
La partie importante est d’avoir une chaîne complète qui fonctionne: ESP32 accessible, flash pilotable, snapshot lisible, IA locale opérationnelle, puis publication MQTT vers Home Assistant.
Galerie à venir
Je compléterai cette section avec de vraies captures et photos de mon installation. Pour l’instant, je garde uniquement les emplacements prévus.
Liens et documentation
Les détails techniques supplémentaires, le code et les évolutions du projet sont disponibles sur GitHub.