đŸ€– Surveillance Intelligente d'Imprimante 3D avec IA Gemini

Version CorrigĂ©e – 25/11/2025
Surveillance Imprimante 3D par IA Google Gemini + Serveur de marquage (FastAPI)


:memo: Description

SystĂšme de surveillance automatique d’imprimante 3D utilisant l’IA Google Gemini pour analyser des captures de la camĂ©ra toutes les 5 minutes pendant l’impression.

L’IA dĂ©tecte les problĂšmes classiques (spaghetti, warping, piĂšce dĂ©collĂ©e, couches manquantes, amas de filament, etc.) et, en cas d’alerte, dessine un cadre rouge autour de la zone suspecte via un petit serveur FastAPI, puis envoie une notification sur votre tĂ©lĂ©phone avec l’image annotĂ©e.


:star: Fonctionnalités principales

:white_check_mark: Analyse IA automatique toutes les X minutes pendant l’impression (par dĂ©faut 5 min)
:camera_with_flash: Détection des problÚmes courants (spaghetti, warping, détachement, débris de filament, sous/sur-extrusion visible, etc.)
:framed_picture: Image annotée avec un rectangle rouge sur la zone problématique (via serveur Gemini Marker)
:mobile_phone: Notifications mobiles avec image cliquable + actions rapides (Pause / ArrĂȘt)
:bar_chart: Statistiques par impression (nombre d’analyses, nombre de problĂšmes, sĂ©ries de problĂšmes consĂ©cutifs)
:bullseye: Notifications de dĂ©but, fin et pause d’impression avec photos


:wrench: Prérequis

  • Home Assistant fonctionnel

  • IntĂ©gration Bambu Lab (ou Ă©quivalent) exposant au minimum :

    • un capteur d’état de l’imprimante (printing, idle, paused, 
)

    • le nom de la tĂąche en cours

    • la camĂ©ra de l’imprimante

    • des boutons Pause / Stop

  • IntĂ©gration Google Generative AI Conversation configurĂ©e avec votre clĂ© Gemini
    (modÚle recommandé : gemini-2.5-flash-lite)

  • Application mobile Home Assistant (Android / iOS)

  • Dossier /config/www/ accessible (pour stocker les images)

  • Un petit conteneur / VM (ex. 192.168.1.7) pour faire tourner le serveur FastAPI Gemini Marker


:clipboard: Mode d’emploi et personnalisation

:one: Entités à personnaliser

Dans le YAML, remplacez les entités suivantes par celles de VOTRE imprimante :

Entité à remplacer Votre entité Description
sensor.3d_imprimante_bambu_p1s_etape_actuelle sensor.VOTRE_IMPRIMANTE_etape_actuelle Statut de l’imprimante (printing, 
)
camera.3d_imprimante_bambu_p1s_camera camera.VOTRE_IMPRIMANTE_camera CamĂ©ra de l’imprimante
sensor.3d_imprimante_bambu_p1s_nom_de_la_tache sensor.VOTRE_IMPRIMANTE_nom_de_la_tache Nom du fichier en impression
button.3d_imprimante_bambu_p1s_arreter_l_impression button.VOTRE_IMPRIMANTE_arreter Bouton ArrĂȘt
button.3d_imprimante_bambu_p1s_mettre_en_pause_l_impression button.VOTRE_IMPRIMANTE_pause Bouton Pause

Adapter aussi si besoin l’IP du serveur FastAPI : http://192.168.1.7:5000/mark.


:two: Service de notification

Dans toutes les notifications, remplacez :

notify.mobile_app_tel_mm

par votre service :

  • notify.mobile_app_VOTRE_TELEPHONE

  • ou tout autre service notify.* que vous utilisez.

Pour le trouver :

  1. Outils de dĂ©veloppement → onglet Services

  2. Cherchez notify.mobile_app_

  3. Notez le nom complet (ex. notify.mobile_app_s25)


:three: Configuration de l’IA Gemini (Google Generative AI Conversation)

  1. Dans Home Assistant :
    ParamĂštres → Appareils et services → Ajouter une intĂ©gration → Google Generative AI Conversation

  2. Saisir votre API Key Gemini.

  3. Dans les options de l’intĂ©gration, choisir le modĂšle (ex. gemini-2.5-flash-lite).

Le package utilise ensuite simplement le service :

action: google_generative_ai_conversation.generate_content

Aucun ai_task ni agent_id à gérer dans ce setup.


:four: FrĂ©quence d’analyse

Par défaut, HA lance une analyse toutes les 5 minutes :

trigger:
  - trigger: time_pattern
    minutes: "/5"

Vous pouvez changer "/5" par :

  • "/3" → toutes les 3 minutes

  • "/10" → toutes les 10 minutes, etc.


:five: Personnalisation du prompt IA

Dans l’automatisation principale, vous avez un prompt dĂ©taillĂ© du type :

prompt: >
  Tu agis comme un contrĂŽleur automatique d'impression 3D.
  ...
  Ne considÚre comme PROBLÈME que les cas suivants :
  - spaghetti / gros amas de filament
  - piÚces décollées du plateau ou qui penchent
  - warping important, coins qui se relĂšvent
  - gros défauts d'adhérence de la premiÚre couche
  - accumulation de débris de filament sur le plateau
  - sous-extrusion ou couches manquantes trĂšs visibles
  - couche supĂ©rieure totalement dĂ©truite ou extrĂȘmement irrĂ©guliĂšre

Vous pouvez :

  • Durcir la dĂ©tection : ajouter d’autres cas dans la liste.

  • L’assouplir : insister sur le fait que des modĂšles trĂšs rugueux (rochers, terrains, dĂ©cors) ne sont pas des dĂ©fauts tant qu’ils sont nets et bien attachĂ©s.


:six: Serveur Gemini Marker (FastAPI)

Ce serveur sert uniquement Ă  dessiner un rectangle rouge sur l’image lorsqu’un problĂšme est dĂ©tectĂ©.

a) Installation rapide (sur 192.168.1.7 par exemple)

apt update
apt install python3 python3-pip -y
pip3 install fastapi uvicorn pillow

b) Script FastAPI

Fichier : /opt/gemini_marker/server.py

from fastapi import FastAPI
from PIL import Image, ImageDraw

app = FastAPI()

@app.post("/mark")
async def mark(data: dict):
    image_path = data["image_path"]
    output_path = data["output_path"]
    bbox = data["bbox"]
    stroke_width = int(data.get("stroke_width", 4))

    img = Image.open(image_path)
    draw = ImageDraw.Draw(img)

    w, h = img.size
    x1 = int(bbox["x_min"] * w)
    y1 = int(bbox["y_min"] * h)
    x2 = int(bbox["x_max"] * w)
    y2 = int(bbox["y_max"] * h)

    draw.rectangle([x1, y1, x2, y2], outline="red", width=stroke_width)
    img.save(output_path)

    return {"status": "ok", "bbox": bbox}

c) Lancement simple

cd /opt/gemini_marker
uvicorn server:app --host 0.0.0.0 --port 5000 &

Dans le YAML HA, on envoie :

  • image_path: /mnt/ha_config/www/imp3d_ai_check.jpg

  • output_path: /mnt/ha_config/www/imp3d_ai_check_marked.jpg

L’image annotĂ©e est ensuite servie par Home Assistant via :
https://VOTRE_HA/local/imp3d_ai_check_marked.jpg


:rocket: Installation

  1. Créez (ou vérifiez) le dossier : /config/www/

  2. Créez le fichier :
    /config/packages/pkg_google_gemini_imp_3d.yaml

  3. Collez le code complet ci-dessous.

  4. Personnalisez les entités (capteurs, boutons, service notify, IP du serveur FastAPI).

  5. Redémarrez Home Assistant.

  6. Lancez une impression test.


:bar_chart: Entités créées

  • input_number.gemini_analyses_current_print – Compteur d’analyses pour l’impression en cours

  • input_number.gemini_problemes_current_print – Compteur de problĂšmes pour l’impression en cours

  • input_number.gemini_problemes_streak – SĂ©rie de problĂšmes consĂ©cutifs

  • input_text.gemini_last_raw – DerniĂšre rĂ©ponse brute (DEBUG)

  • input_text.gemini_last_response – Dernier rĂ©sumĂ© lisible

  • input_text.gemini_current_print_name – Nom de l’impression en cours

  • input_datetime.gemini_last_check – Horodatage de la derniĂšre vĂ©rification

  • sensor.gemini_total_analyses_count / sensor.gemini_total_problems_count – Stats globales sur le log

  • sensor.gemini_total_analyses_history / sensor.gemini_total_problemes_history – Stats historisĂ©es

  • binary_sensor.gemini_probleme_en_cours – Indicateur binaire de problĂšme IA

  • sensor.imprimante_3d_-_statut_lisible – Statut lisible de l’imprimante (En impression / Inactive / En pause)


:page_facing_up: Code complet – PACKAGE HA + IA + Marquage

# =============================================================================
# PACKAGE : Surveillance Imprimante 3D + Gemini (Bambu P1S)
# FICHIER : /config/packages/pkg_google_gemini_imp_3d.yaml
# VERSION : 2.0
# =============================================================================

# -----------------------------------------------------------------------------
# SHELL COMMANDS : log texte + purge
# -----------------------------------------------------------------------------
shell_command:
  gemini_log_append: >
    sh -c 'printf "%s\n" "$1" >> /config/www/imp3d_gemini_history.log' --
    "{{ message }}"

  purge_gemini_current_log: ">: /config/www/imp3d_gemini_history.log"

# -----------------------------------------------------------------------------
# REST COMMAND : FastAPI marquage d’image (bbox dynamique)
# -----------------------------------------------------------------------------
rest_command:
  gemini_mark_image:
    url: "http://192.168.1.7:5000/mark"   # ← adapter l'IP si besoin
    method: POST
    headers:
      Content-Type: application/json
    payload: >
      {
        "image_path": "/mnt/ha_config/www/imp3d_ai_check.jpg",
        "output_path": "/mnt/ha_config/www/imp3d_ai_check_marked.jpg",
        "bbox": {
          "x_min": {{ x_min | default(0.45) }},
          "y_min": {{ y_min | default(0.45) }},
          "x_max": {{ x_max | default(0.55) }},
          "y_max": {{ y_max | default(0.55) }}
        },
        "stroke_width": 4,
        "margin": 0.0
      }

# -----------------------------------------------------------------------------
# INPUTS (compteurs + derniers résultats)
# -----------------------------------------------------------------------------
input_number:
  gemini_analyses_current_print:
    name: "Gemini - Analyses (impression actuelle)"
    min: 0
    max: 1000
    step: 1
    icon: mdi:counter

  gemini_problemes_current_print:
    name: "Gemini - ProblÚmes détectés (impression actuelle)"
    min: 0
    max: 100
    step: 1
    icon: mdi:alert-circle

  gemini_problemes_streak:
    name: "Gemini - Série de problÚmes consécutifs"
    min: 0
    max: 50
    step: 1
    icon: mdi:alert-decagram

input_text:
  gemini_last_raw:
    name: "Gemini - DerniÚre réponse brute (DEBUG)"
    max: 255
    icon: mdi:code-json

  gemini_last_response:
    name: "Gemini - Dernier résumé lisible"
    max: 255
    icon: mdi:message-text

  gemini_current_print_name:
    name: "Gemini - Nom impression"
    max: 100
    icon: mdi:printer-3d

input_datetime:
  gemini_last_check:
    name: "Gemini - DerniÚre vérification"
    has_date: true
    has_time: true

input_boolean:
  gemini_package_loaded:
    name: "DEBUG - Gemini package chargé"
    icon: mdi:bug

# -----------------------------------------------------------------------------
# SCRIPT : consultation de l’historique (log)
# -----------------------------------------------------------------------------
script:
  voir_historique_gemini:
    alias: "📖 Voir Historique Gemini"
    mode: single
    sequence:
      - action: homeassistant.update_entity
        target:
          entity_id:
            - sensor.gemini_total_analyses_count
            - sensor.gemini_total_problems_count
      - delay:
          seconds: 1
      - variables:
          total_lines: "{{ states('sensor.gemini_total_analyses_count') | int(0) }}"
          total_problems: "{{ states('sensor.gemini_total_problems_count') | int(0) }}"
      - action: persistent_notification.create
        data:
          title: "📖 Historique Gemini"
          message: |
            📊 Total analyses (lignes log): {{ total_lines }}
            ⚠ Total problĂšmes (lignes log): {{ total_problems }}

            Fichier actuel: /config/www/imp3d_gemini_history.log
            Accessible via: /local/imp3d_gemini_history.log

# -----------------------------------------------------------------------------
# SENSORS COMMAND_LINE : stats depuis le fichier log
# -----------------------------------------------------------------------------
command_line:
  - sensor:
      name: "Gemini Total Analyses Count"
      unique_id: gemini_total_analyses_count
      command: "wc -l < /config/www/imp3d_gemini_history.log 2>/dev/null || echo 0"
      scan_interval: 300

  - sensor:
      name: "Gemini Total Problems Count"
      unique_id: gemini_total_problems_count
      command: "grep -c 'PROBLÈME' /config/www/imp3d_gemini_history.log 2>/dev/null || echo 0"
      scan_interval: 300

# -----------------------------------------------------------------------------
# TEMPLATE SENSORS + BINARY_SENSOR
# -----------------------------------------------------------------------------
template:
  - sensor:
      - name: "Imprimante 3D - Statut Lisible"
        unique_id: p1s_status_readable
        state: >
          {% set s = states('sensor.3d_imprimante_bambu_p1s_etape_actuelle') %}
          {% if s == 'printing' %}En impression
          {% elif s == 'idle' %}Inactive
          {% elif 'paused' in s %}En pause
          {% else %}{{ s | replace('_', ' ') | title }}{% endif %}
        icon: mdi:printer-3d

      - name: "Gemini - Total Analyses Historique"
        unique_id: gemini_total_analyses_history
        state: "{{ states('sensor.gemini_total_analyses_count') | int(0) }}"
        icon: mdi:counter
        unit_of_measurement: "analyses"

      - name: "Gemini - Total ProblĂšmes Historique"
        unique_id: gemini_total_problemes_history
        state: "{{ states('sensor.gemini_total_problems_count') | int(0) }}"
        icon: mdi:alert-circle
        unit_of_measurement: "problĂšmes"

    binary_sensor:
      - name: "Gemini - ProblĂšme en cours"
        unique_id: gemini_probleme_en_cours
        state: >
          {{ 'PROBLEME' in states('input_text.gemini_last_response') }}
        icon: mdi:alert-octagram

# -----------------------------------------------------------------------------
# AUTOMATISATIONS
# -----------------------------------------------------------------------------
automation:
  - alias: "đŸ§Ș TEST - Analyse Gemini Manuelle"
    id: test_gemini_manual_trigger
    trigger:
      - trigger: event
        event_type: test_gemini_analysis
    action:
      - action: shell_command.gemini_log_append
        data:
          message: "TEST MANUEL OK"

  - alias: "🔍 Surveillance Impression 3D - IA Gemini"
    id: p1s_ai_monitoring_gemini
    mode: queued
    trigger:
      - trigger: time_pattern
        minutes: "/5"
    condition:
      - condition: template
        value_template: >
          {{ states('sensor.3d_imprimante_bambu_p1s_etape_actuelle')
             not in ['idle','offline','unknown'] }}
    action:
      - action: camera.snapshot
        target:
          entity_id: camera.3d_imprimante_bambu_p1s_camera
        data:
          filename: /config/www/imp3d_ai_check.jpg

      - delay:
          seconds: 2

      - action: google_generative_ai_conversation.generate_content
        continue_on_error: true
        data:
          prompt: >
            Tu agis comme un contrĂŽleur automatique d'impression 3D.
            Tu reçois UNE SEULE photo d'une imprimante 3D en cours d'impression.

            Le modĂšle peut ĂȘtre volontairement trĂšs rugueux ou irrĂ©gulier
            (rocher, pierre, terrain, décor, sculpture organique, etc.).
            Une surface globalement rugueuse mais cohérente, bien attachée
            et sans filaments parasites n'est PAS un défaut.

            Ne considÚre comme PROBLÈME que les cas suivants :
            - spaghetti / gros amas de filament
            - piÚces décollées du plateau ou qui penchent
            - warping important, coins qui se relĂšvent
            - gros défauts d'adhérence de la premiÚre couche
            - accumulation de débris de filament sur le plateau
            - sous-extrusion ou couches manquantes trĂšs visibles
            - couche supĂ©rieure totalement dĂ©truite ou extrĂȘmement irrĂ©guliĂšre

            Réponds STRICTEMENT avec l'un de ces deux JSON, sans texte autour :

            1) Si tout va bien :
            {"status":"OK"}

            2) S'il y a un problĂšme visible :
            {
              "status": "PROBLEME",
              "description": "texte court en français décrivant le problÚme",
              "bbox": {
                "x_min": 0.0,
                "y_min": 0.0,
                "x_max": 1.0,
                "y_max": 1.0
              }
            }

            Ne rajoute pas de ```json``` ni de commentaires.
          filenames:
            - /config/www/imp3d_ai_check.jpg
        response_variable: gemini_response

      - variables:
          gemini_raw: >
            {{ gemini_response.text if gemini_response is defined
               else 'ERREUR_APPEL_GEMINI' }}

          gemini_clean: >
            {% set raw = gemini_raw | string %}
            {% set raw = raw | replace('```json','') | replace('```','') %}
            {% set raw = raw | regex_replace('\\s+', ' ') %}
            {{ raw | trim }}

          gemini_json: >
            {% if gemini_clean.startswith('{') and gemini_clean.endswith('}') %}
              {{ gemini_clean | from_json(default={}) }}
            {% else %}
              {}
            {% endif %}

          gemini_status: >
            {% if gemini_json is mapping and gemini_json.status is defined %}
              {% set s = gemini_json.status | string | upper %}
              {% if s in ['OK', 'PROBLEME'] %}
                {{ s }}
              {% else %}
                ERREUR
              {% endif %}
            {% elif gemini_raw == 'ERREUR_APPEL_GEMINI' %}
              ERREUR
            {% elif '"status"' in gemini_raw %}
              {% if 'PROBLEME' in gemini_raw %}PROBLEME
              {% elif 'OK' in gemini_raw %}OK
              {% else %}ERREUR{% endif %}
            {% else %}
              ERREUR
            {% endif %}

          gemini_description: >
            {% if gemini_status == 'PROBLEME' %}
              {% if gemini_json is mapping and gemini_json.description is defined %}
                {{ gemini_json.description }}
              {% else %}
                ProblÚme détecté sur l'impression.
              {% endif %}
            {% elif gemini_status == 'OK' %}
              OK
            {% else %}
              Erreur d’analyse IA
            {% endif %}

          gemini_bbox_x_min: >
            {% if gemini_status == 'PROBLEME'
                  and gemini_json is mapping
                  and gemini_json.bbox is defined
                  and gemini_json.bbox.x_min is defined %}
              {{ gemini_json.bbox.x_min }}
            {% else %}
              0.45
            {% endif %}

          gemini_bbox_y_min: >
            {% if gemini_status == 'PROBLEME'
                  and gemini_json is mapping
                  and gemini_json.bbox is defined
                  and gemini_json.bbox.y_min is defined %}
              {{ gemini_json.bbox.y_min }}
            {% else %}
              0.45
            {% endif %}

          gemini_bbox_x_max: >
            {% if gemini_status == 'PROBLEME'
                  and gemini_json is mapping
                  and gemini_json.bbox is defined
                  and gemini_json.bbox.x_max is defined %}
              {{ gemini_json.bbox.x_max }}
            {% else %}
              0.55
            {% endif %}

          gemini_bbox_y_max: >
            {% if gemini_status == 'PROBLEME'
                  and gemini_json is mapping
                  and gemini_json.bbox is defined
                  and gemini_json.bbox.y_max is defined %}
              {{ gemini_json.bbox.y_max }}
            {% else %}
              0.55
            {% endif %}

          gemini_ts: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}"

          gemini_resume: >
            {% if gemini_status == 'PROBLEME' %}
              {{ gemini_ts }} - PROBLEME - {{ gemini_description }}
            {% elif gemini_status == 'OK' %}
              {{ gemini_ts }} - OK
            {% else %}
              {{ gemini_ts }} - ERREUR - {{ gemini_description }}
            {% endif %}

      - action: input_number.set_value
        target:
          entity_id: input_number.gemini_analyses_current_print
        data:
          value: "{{ states('input_number.gemini_analyses_current_print') | float + 1 }}"

      - choose:
          - conditions:
              - condition: template
                value_template: "{{ gemini_status == 'PROBLEME' }}"
            sequence:
              - action: input_number.set_value
                target:
                  entity_id: input_number.gemini_problemes_streak
                data:
                  value: "{{ states('input_number.gemini_problemes_streak') | float + 1 }}"
          - conditions:
              - condition: template
                value_template: "{{ gemini_status != 'PROBLEME' }}"
            sequence:
              - action: input_number.set_value
                target:
                  entity_id: input_number.gemini_problemes_streak
                data:
                  value: 0

      - action: input_text.set_value
        target:
          entity_id: input_text.gemini_last_raw
        data:
          value: "{{ gemini_raw }}"

      - action: input_text.set_value
        target:
          entity_id: input_text.gemini_last_response
        data:
          value: "{{ gemini_resume }}"

      - action: input_datetime.set_datetime
        target:
          entity_id: input_datetime.gemini_last_check
        data:
          datetime: "{{ now().isoformat() }}"

      - action: shell_command.gemini_log_append
        data:
          message: >
            {% if gemini_status == "OK" %}✅ OK
            {% elif gemini_status == "PROBLEME" %}⚠ PROBLÈME
            {% else %}❌ ERREUR GEMINI{% endif %}
            | {{ now().strftime('%Y-%m-%d %H:%M:%S') }}
            | {{ states('input_text.gemini_current_print_name') }}
            | Analyse #{{ states('input_number.gemini_analyses_current_print') | int }}
            | {{ gemini_description }}

      - condition: template
        value_template: "{{ gemini_status == 'PROBLEME' }}"

      - action: rest_command.gemini_mark_image
        data:
          x_min: "{{ gemini_bbox_x_min }}"
          y_min: "{{ gemini_bbox_y_min }}"
          x_max: "{{ gemini_bbox_x_max }}"
          y_max: "{{ gemini_bbox_y_max }}"

      - action: input_number.set_value
        target:
          entity_id: input_number.gemini_problemes_current_print
        data:
          value: "{{ states('input_number.gemini_problemes_current_print') | float + 1 }}"

      - action: notify.mobile_app_tel_mm
        data:
          title: "⚠ ProblĂšme Impression 3D !"
          message: "{{ gemini_description }}"
          data:
            image: "https://xxxxxxxxx.org/local/imp3d_ai_check_marked.jpg?t={{ now().timestamp() | int }}"
            clickAction: "/local/imp3d_ai_check_marked.jpg?t={{ now().timestamp() | int }}"
            priority: high
            ttl: 0
            color: "#FF0000"
            tag: "print_issue"
            actions:
              - action: "PAUSE_PRINT"
                title: "⏞ Pause"
              - action: "STOP_PRINT"
                title: "🛑 ArrĂȘter"

  - alias: "⏞ Auto-pause impression 3D si problĂšmes rĂ©pĂ©tĂ©s"
    id: p1s_auto_pause_gemini
    trigger:
      - trigger: numeric_state
        entity_id: input_number.gemini_problemes_streak
        above: 2
    condition:
      - condition: state
        entity_id: sensor.3d_imprimante_bambu_p1s_etape_actuelle
        state: "printing"
    action:
      - action: button.press
        target:
          entity_id: button.3d_imprimante_bambu_p1s_mettre_en_pause_l_impression
      - action: notify.mobile_app_tel_mm
        data:
          title: "⏞ Pause automatique par IA"
          message: >
            Impression "{{ states('input_text.gemini_current_print_name') }}"
            mise en PAUSE aprĂšs {{ states('input_number.gemini_problemes_streak') | int }}
            analyses problématiques consécutives.
          data:
            priority: high
            color: "#FFA500"
            tag: "print_issue_auto_pause"

  - alias: "🛑 Auto-stop impression 3D si problùmes persistants"
    id: p1s_auto_stop_gemini
    trigger:
      - trigger: numeric_state
        entity_id: input_number.gemini_problemes_streak
        above: 5
    condition:
      - condition: state
        entity_id: sensor.3d_imprimante_bambu_p1s_etape_actuelle
        state: "printing"
    action:
      - action: button.press
        target:
          entity_id: button.3d_imprimante_bambu_p1s_arreter_l_impression
      - action: notify.mobile_app_tel_mm
        data:
          title: "🛑 ArrĂȘt automatique par IA"
          message: >
            Impression "{{ states('input_text.gemini_current_print_name') }}"
            arrĂȘtĂ©e automatiquement aprĂšs
            {{ states('input_number.gemini_problemes_streak') | int }}
            analyses problématiques consécutives.
          data:
            priority: high
            color: "#FF0000"
            tag: "print_issue_auto_stop"

  - alias: "đŸ–šïž DĂ©but Surveillance Impression 3D"
    id: print_start_gemini_notification
    trigger:
      - trigger: state
        entity_id: sensor.3d_imprimante_bambu_p1s_etape_actuelle
        to: "printing"
    action:
      - action: shell_command.purge_gemini_current_log

      - action: input_number.set_value
        target:
          entity_id:
            - input_number.gemini_analyses_current_print
            - input_number.gemini_problemes_current_print
            - input_number.gemini_problemes_streak
        data:
          value: 0

      - action: input_text.set_value
        target:
          entity_id:
            - input_text.gemini_last_raw
            - input_text.gemini_last_response
        data:
          value: ""

      - action: input_text.set_value
        target:
          entity_id: input_text.gemini_current_print_name
        data:
          value: "{{ states('sensor.3d_imprimante_bambu_p1s_nom_de_la_tache') }}"

      - action: shell_command.gemini_log_append
        data:
          message: >
            đŸ–šïž DÉBUT
            | {{ now().strftime('%Y-%m-%d %H:%M:%S') }}
            | {{ states('sensor.3d_imprimante_bambu_p1s_nom_de_la_tache') }}
            | Analyse #0
            | Impression démarrée - Surveillance IA activée

      - action: camera.snapshot
        target:
          entity_id: camera.3d_imprimante_bambu_p1s_camera
        data:
          filename: /config/www/imp3d_start.jpg

      - delay:
          seconds: 2

      - action: notify.mobile_app_tel_mm
        data:
          title: "đŸ–šïž Impression DĂ©marrĂ©e"
          message: "{{ states('sensor.3d_imprimante_bambu_p1s_nom_de_la_tache') }} - Surveillance IA activée"
          data:
            image: "/api/camera_proxy/camera.3d_imprimante_bambu_p1s_camera?{{ now().timestamp() | int }}"
            clickAction: "/api/camera_proxy/camera.3d_imprimante_bambu_p1s_camera?{{ now().timestamp() | int }}"
            priority: normal
            color: "#0000FF"
            tag: "print_status"

  - alias: "✅ Fin Impression 3D RĂ©ussie"
    id: print_completed_gemini_notification
    trigger:
      - trigger: state
        entity_id: sensor.3d_imprimante_bambu_p1s_etape_actuelle
        to: "idle"
        from: "printing"
    action:
      - action: shell_command.gemini_log_append
        data:
          message: >
            ✅ FIN
            | {{ now().strftime('%Y-%m-%d %H:%M:%S') }}
            | {{ states('input_text.gemini_current_print_name') }}
            | Analyse #{{ states('input_number.gemini_analyses_current_print') | int }}
            | Impression terminée - {{ states('input_number.gemini_analyses_current_print') | int }} analyses - {{ states('input_number.gemini_problemes_current_print') | int }} problÚmes détectés

      - action: camera.snapshot
        target:
          entity_id: camera.3d_imprimante_bambu_p1s_camera
        data:
          filename: /config/www/imp3d_final.jpg

      - delay:
          seconds: 2

      - action: notify.mobile_app_tel_mm
        data:
          title: "✅ Impression TerminĂ©e"
          message: >
            {{ states('input_text.gemini_current_print_name') }}

            📊 Analyses IA: {{ states('input_number.gemini_analyses_current_print') | int }}
            ⚠ ProblĂšmes: {{ states('input_number.gemini_problemes_current_print') | int }}
          data:
            image: "/local/imp3d_final.jpg?t={{ now().timestamp() | int }}"
            clickAction: "/local/imp3d_final.jpg?t={{ now().timestamp() | int }}"
            priority: high
            ttl: 0
            color: "#00FF00"
            tag: "print_status"


:light_bulb: Checklist avant test

  • Toutes les entitĂ©s sensor.3d_imprimante_bambu_p1s_* remplacĂ©es

  • Toutes les entitĂ©s camera.3d_imprimante_bambu_p1s_camera remplacĂ©es

  • Boutons Pause / Stop adaptĂ©s

  • Service notify.mobile_app_tel_mm remplacĂ©

  • IP du serveur FastAPI corrigĂ©e si besoin

  • Fichier sauvegardĂ© dans /config/packages/

  • Home Assistant redĂ©marrĂ©

  • Une impression test lancĂ©e (attendre au moins 5 min pour la premiĂšre analyse)


:lady_beetle: Dépannage rapide

  • Pas de notification
    → VĂ©rifier le service notify.* dans Outils de dĂ©veloppement.

  • Aucune analyse ne se lance
    → VĂ©rifier que l’état de l’imprimante passe bien Ă  printing.
    → VĂ©rifier le dĂ©clencheur time_pattern (minutes /5).

  • Message ERREUR_APPEL_GEMINI
    → VĂ©rifier l’intĂ©gration Google Generative AI Conversation (clĂ© API, quotas, modĂšle).

  • Pas de cadre rouge sur l’image
    → Tester le FastAPI Ă  part, vĂ©rifier le chemin des fichiers (/mnt/ha_config/www/...) et les droits.

Une fois tout ça en place, tu es tranquille : l’IA surveille tes impressions, t’envoie les captures comme celles que tu as dĂ©jĂ  vu (plateau avec cadre rouge bien centrĂ©) et te spamme uniquement quand il y a un vrai souci.

2 « J'aime »

Salut, excellent, merci pour le partage.
Tu sais si un compte gratuit pour gemini suffit ou il faut prendre un abonnement ?
On a le droit Ă  combien de requĂȘte avec un compte gratuit ?

J’ai une P1S aussi, si j’ai le temps je test à la prochaine impression

Bonjour,

oui cela suffit :

Quota gratuit de Google Gemini

Google offre un quota gratuit et gĂ©nĂ©reux pour l’API Gemini :

Limites gratuites (API Gemini)

  • 15 requĂȘtes par minute (RPM)

  • 1 500 requĂȘtes par jour (RPD)

  • 1 million de tokens par jour

Pour ton usage (analyse toutes les 5 minutes)

  • 12 analyses par heure = ~288 analyses par jour

  • :white_check_mark: Largement dans les limites gratuites !


:gear: Configuration recommandée pour rester gratuit

:one: Utiliser le bon modĂšle

Dans Home Assistant, configure l’intĂ©gration Google Generative AI Conversation avec :
ModĂšle : gemini-2.0-flash-exp (gratuit)

:two: Obtenir une clé API gratuite

  1. Va sur Google AI Studio

  2. Crée une API Key gratuite

  3. :white_check_mark: Utilise cette clé dans Home Assistant

Important :

  • N’active PAS la facturation sur ton compte Google Cloud

  • Reste sur le tier « Free Â» de l’API

merci pour les précisions !

Vous avez quoi comme imprimante ?

j’ai une P1S Ă©galement

Bonjour @Elmago, fĂ©licitation pour ton travail. J’ai une BambuLab A1. Je ne comprends pas une de partie du tuto.

La partie 3 Configuration de l’IA Gemini la configuration du service se fait dans le fichier yaml créé dans la partie 5 que l’on a positionnĂ© ici

/config/packages/pkg_google_gemini_imp_3d.yaml

Tout d’abord, une super idĂ©e !!! Et aussi merci pour le partage.

J’ai moi aussi une A1 et je bloque Ă  l’étape 3.

Dans le script, tu ne peux prĂ©ciser l’agent-id car la ligne n’est pas prĂ©sente :

- action: ai_task.generate_data
        data:
          prompt: "Tu es un expert en impression 3D. Analyse cette image d'une imprimante 3D en cours d'impression. Détecte ces problÚmes : spaghetti (impression détachée), warping (coins décollés), détachement du plateau, sous-extrusion (lignes fines), sur-extrusion (trop de plastique), problÚme de premiÚre couche. Si TOUT VA BIEN, réponds UNIQUEMENT par le mot 'OK'. Si tu détectes un problÚme, réponds 'PROBLÈME: [description courte en une phrase]'."
          filenames:
            - /config/www/p1s_ai_check.jpg

Ceci dit, je n’arrive pas Ă  le retrouver. Je n’ai pas ParamĂštres → Extensions et services et pourtant j’ai bien le mode avancĂ© activĂ©.

Peut-ĂȘtre n’est ce pas nĂ©cessaire au final ?

[EDIT]

Donc c’est nĂ©cessaire mais j’ai adaptĂ© l’automatisation et le format de la rĂ©ponse qui ne collait pas.

Voici mon automatisation :

alias: 🔍 Surveillance Impression 3D - IA Gemini
description: ""
triggers:
  - trigger: time_pattern
    minutes: /5
conditions:
  - condition: state
    entity_id: sensor.bambulaba1_etape_actuelle
    state: printing
actions:
  - action: camera.snapshot
    target:
      entity_id: camera.bambulaba1_camera
    data:
      filename: /config/www/p1s_ai_check.jpg
  - delay:
      seconds: 2
  - action: input_number.set_value
    target:
      entity_id: input_number.gemini_analyses_current_print
    data:
      value: "{{ states('input_number.gemini_analyses_current_print') | float + 1 }}"
  - action: ai_task.generate_data
    metadata: {}
    data:
      entity_id: ai_task.google_ai_task
      task_name: Analyse de l'image 3D
      instructions: >-
        Tu es un expert en impression 3D. Analyse cette image d'une imprimante
        3D en
          cours d'impression. Détecte ces problÚmes : spaghetti (impression détachée),
          warping (coins décollés), détachement du plateau, sous-extrusion (lignes
          fines), sur-extrusion (trop de plastique), problĂšme de premiĂšre couche. Si
          TOUT VA BIEN, réponds UNIQUEMENT par le mot 'OK'. Si tu détectes un problÚme,
          réponds 'PROBLÈME: [description courte en une phrase]'.
      attachments:
        media_content_id: media-source://camera/camera.bambulaba1_camera
        media_content_type: image/jpeg
        metadata:
          title: BambulabA1 Caméra
          thumbnail: /api/camera_proxy/camera.bambulaba1_camera
          media_class: video
          children_media_class: null
          navigateIds:
            - {}
            - media_content_type: app
              media_content_id: media-source://camera
    response_variable: gemini_response
  - action: ai_task.generate_data
    data:
      prompt: >-
        Tu es un expert en impression 3D. Analyse cette image d'une imprimante
        3D en cours d'impression. Détecte ces problÚmes : spaghetti (impression
        détachée), warping (coins décollés), détachement du plateau,
        sous-extrusion (lignes fines), sur-extrusion (trop de plastique),
        problÚme de premiÚre couche. Si TOUT VA BIEN, réponds UNIQUEMENT par le
        mot 'OK'. Si tu détectes un problÚme, réponds 'PROBLÈME: [description
        courte en une phrase]'.
      filenames:
        - /config/www/p1s_ai_check.jpg
    response_variable: gemini_response
    enabled: false
  - action: input_text.set_value
    target:
      entity_id: input_text.gemini_last_response
    data:
      value: "{{ gemini_response.data }}"
  - action: input_datetime.set_datetime
    target:
      entity_id: input_datetime.gemini_last_check
    data:
      datetime: "{{ now().isoformat() }}"
  - action: system_log.write
    data:
      message: "Gemini: {{ gemini_response.data }}"
      level: info
  - condition: template
    value_template: "{{ 'OK' not in  gemini_response.data }}"
  - action: input_number.set_value
    target:
      entity_id: input_number.gemini_problemes_current_print
    data:
      value: "{{ states('input_number.gemini_problemes_current_print') | float + 1 }}"
  - action: notify.mobile_app_s25
    data:
      title: ⚠ ProblĂšme Impression 3D !
      message: "{{ gemini_response.data }}"
      data:
        image: /local/p1s_ai_check.jpg?t={{ now().timestamp() | int }}
        priority: high
        ttl: 0
        color: "#FF0000"
        tag: print_issue
        actions:
          - action: PAUSE_PRINT
            title: ⏞ Pause
          - action: STOP_PRINT
            title: 🛑 ArrĂȘter
mode: single

Pour l’instant, rien Ă  signaler. On verra au prochain Ă©chec ce que ça donne.

Bonjour !

Je comprends votre confusion, c’est effectivement pas trùs clair dans le tutoriel. Laissez-moi vous expliquer :

Ce qu’il faut faire

Étape 1 : Trouver votre agent_id Gemini

  1. Allez dans Paramùtres → Appareils et services

  2. Cherchez Google Generative AI Conversation

  3. Cliquez dessus, vous verrez quelque chose comme : conversation.gemini_1_5_flash ou conversation.gemini_1_5_pro

  4. Notez cet identifiant, c’est votre agent_id

Étape 2 : Modifier le fichier YAML

Dans le fichier /config/packages/pkg_google_gemini_imp_3d.yaml, cherchez cette section (ligne ~60-65) :

action: ai_task.generate_data
data:
agent_id: conversation.VOTRE_AGENT_GEMINI # ← ICI
prompt: « Tu es un expert en impression 3D
 »
filenames:
- /config/www/p1s_ai_check.jpg

Remplacez conversation.VOTRE_AGENT_GEMINI par l’identifiant que vous avez notĂ© Ă  l’étape 1.

**Exemple concret :
**

agent_id: conversation.gemini_1_5_flash

Résumé

  • Partie 3 = Vous donne les instructions pour trouver votre agent_id

  • Partie 5 = Le fichier YAML complet oĂč vous devez appliquer cette modification

C’est juste une information Ă  rĂ©cupĂ©rer dans l’interface Home Assistant, puis Ă  reporter dans le fichier YAML avant de redĂ©marrer HA.

Est-ce plus clair maintenant ? :blush:

1 « J'aime »

@Elmago déja merci pour le partage, je vais réaliser la conf !

Si je comprends bien tu utilises la camera native de la P1S. cela fonctionne bien ?

1 « J'aime »

Oui ca fonctionne avec la camĂ©ra native, je prend un cluchĂ© toute les 5 minutes, mais comme c’est une imprimante top, pour le moment j’ai pas de pb avec donc


Mon but est plus d’avoir une alerte quand je suis pas la pour les “Gros PB” dĂ©collement par exemple.

1 « J'aime »

J’ai retentĂ© avec ta partie AI_task :

action: ai_task.generate_data
data:
  agent_id: conversation.google_ai_conversation
  prompt: >-
    Tu es un expert en impression 3D. Analyse cette image d'une imprimante 3D en
    cours d'impression. Détecte ces problÚmes : spaghetti (impression détachée),
    warping (coins décollés), détachement du plateau, sous-extrusion (lignes
    fines), sur-extrusion (trop de plastique), problĂšme de premiĂšre couche. Si
    TOUT VA BIEN, réponds UNIQUEMENT par le mot 'OK'. Si tu détectes un
    problÚme, réponds 'PROBLÈME: [description courte en une phrase]'.
  filenames:
    - /config/www/p1s_ai_check.jpg
response_variable: gemini_response

Mais j’ai l’erreur suivante : Erreur : extra keys not allowed @ data[â€č agent_id â€ș]

Sinon, avec l’autre AI_task, tout fonctionne sans pb. J’ai eu un dĂ©faut de dĂ©collement et cela me l’a remontĂ© !

Merci bcp pour ton partage.

Ok car la camĂ©ra n’est pas top mais je vais tester.

Sur la P1S j’ai eu du dĂ©collement avec le plateau smooth. AprĂšs il m’arrive parfois d’avoir des spaghettis en fonction du filament ou de certaines conditions. Dans l’ensemble cela reste rare.

Mais bon j’ai un pb sur une impression de 9h et je ne m’en suis rendu compte que le matin alors que le pb avait eu lieu au dĂ©but de l’impression :sweat_smile:

Merci @Elmago pour ta réponse. Je viens de corriger mon yaml et je teste une impression 3D ce soir en rentrant.

encore un grand merci pour ton travail et ce partage. Gemini c’est aussi nouveau pour moi, j’hĂ©sitais Ă  l’employer dans ma domotique.

En fait j’ai vu dans les journaux de HA j’ai la mĂȘme erreur que toi.

Tu as trouvé une solution ?
Le Chat me dit d’enlever agent_id: conversation.google_ai_conversation

Ok je suis passé à la méthode B et cela fonctionne.

Je suis en train de travailler sur cette partie car imprimangt 24/24 j’ai cassĂ© les quotas gemini. :slight_smile:
Je reviens vers vous quand tout sera tester

Je viens de mettre une autre version, qui semble t"‘il fonctionne chez moi.