🌱 GrowManager : L'appli de gestion de culture (mais pas que)


Messages recommandés

Posté(e) (modifié)

Bonjour a tous

 

Allez reprenons la suite de la présentation avec la page suivante
 

6 - Les Extractions - Rosin & Hash

 

Ce que c'est
GrowManager intègre deux pages dédiées aux extractions : une pour la Rosin et une pour le Hash. L'idée est la même dans les deux cas - enregistrer chaque session, suivre vos rendements, et accumuler des stats pour progresser d'une session à l'autre.

 

Extractions Rosin
La liste des sessions
Toutes vos sessions de presse sont listées dans un tableau triable : variété, maillage, nombre de passes, poids d'entrée, poids de sortie, rendement en %, date. Triable sur toutes ces colonnes, avec tri par défaut sur la date (les plus récentes en premier).
Une barre de recherche vous permet de filtrer par variété - et quand vous filtrez, les statistiques en haut de page se recalculent automatiquement pour ne refléter que les sessions de la variété sélectionnée. Pratique pour comparer les rendements d'un même strain sur plusieurs sessions.

1774584802_06-ExtractionsRosin.thumb.png.8de640631f93e997b3b55484c9ce7ea4.png

 

Les stats
Quatre cartes en haut de page :

  • Rendement moyen
  • Total pressé (poids d'entrée cumulé)
  • Rosin extrait (poids de sortie cumulé)
  • Nombre de sessions

Ces chiffres s'adaptent au filtre variété si vous en avez un d'actif.


1449475704_06-ExtractionRosin-Statsparvarieteetparanne.thumb.png.342765b583b28ff490564393ab4ee01a.png

 

Enregistrer une session
Quand vous lancez une nouvelle session, vous renseignez :

  • La variété pressée
  • Le maillage du rosin bag
  • Le nombre de passes
  • Le poids de chaque sac individuellement (en grammes avec deux décimales) - le total d'entrée se calcule automatiquement
  • Le poids de sortie (rosin récupéré)
  • La température de presse
  • Des notes libres

Le rendement est calculé automatiquement.

1664060987_06-ExtractionRosin-ModalAjoutExtraction1.thumb.png.28a687ef9de2ab7090a69201881ceeaf.png1081693828_06-ExtractionRosin-ModalAjoutExtraction2.thumb.png.2fbcce483d15604b5533ce3f746f66d7.png

 

Le détail d'une session
Chaque session est cliquable pour accéder à sa fiche complète - tous les paramètres enregistrés, les poids par sac, le rendement calculé, les notes.
913676626_06-ExtractionRosin-ModalDetailExtraction.thumb.png.81fe51b173d1ad833476b7ce585bbe20.png

 

Extractions Hash
La page Hash fonctionne sur le même principe mais adaptée aux techniques de tamisage à sec (Polinator) et à l'eau (Ice-o-lator).
350743632_06-ExtractionHash.thumb.png.4e790f40eb111f00d40048d5d2826e3f.png

 

Vous enregistrez vos sessions avec :

la variété, la technique utilisée, le nombre de passages, les sacs et leurs rendements, les notes.

Même système de filtre par variété, même logique de stats cumulées.


Ice-O-Lator :

1232043922_06-ExtractionHash-IceOLator2.thumb.png.5f7283672d4ae1b69f522fdd3ae964f9.png892523844_06-ExtractionHash-IceOLator3.thumb.png.5d4790b3f1947f4d4e45cce91a9657c0.png

 

Polinator (mais marche aussi pour les tamis)

1781731505_06-ExtractionHash-Pollinator.png.69b35edf29d168d53de5e84371b2acba.png

 

N'ayant pas eu l'occasion de faire du hash dernièrement je n'ai pas eu l'occasion de tester les fonctionnalités de cette page en conditions réelles donc il manque surement plein de choses

 

Les deux pages extractions sont aussi alimentées par l'onglet "Pour extraction" de la page Stock - vous savez toujours exactement ce que vous avez en attente de traitement et depuis combien de temps.

 

D'autres options d'extractions seront ajoutées dans le futur (BHO, etc...)

 

A+

Hey bonjour a tous,

 

@Lamic-Tal : Désolé j'avais zappé de te répondre. L'intégration des outils bluetooth est pas prete d'arrivée. Je sais meme pas si c'est possible. Je vais ajouter des options pour d'autres outils mais uniquement via wifi pour le moment.

@Maledikchaouch encore merci pour tous tes retours.

- Pour le Dashboard c'est pas normal il doit s'afficher meme si tu n'a rien. Il s'affiche juste avec des valeurs a 0 partout mais il doit t'afficher les modules.
- Tres bonne idée pour les clones je vais bosser dessus et je te tiens au jus

- Tres bonne idée aussi pour les croisements je vais bosser dessus aussi

 

A+

Modifié par mdf73
  • Like 1
  • Thanks 6
Lien à poster
Partager sur d’autres sites

Hello dominical !

 

Il y a 21 heures, mdf73 a dit:

- Pour le Dashboard c'est pas normal il doit s'afficher meme si tu n'a rien. Il s'affiche juste avec des valeurs a 0 partout mais il doit t'afficher les modules.

 

nodash.thumb.PNG.923689c807bbee8f8ab35b6abed82b5e.PNG

 

J'ai suivi tes instructions (pas le choix vu mon expertise dans le domaine), c'est donc ta faute 🤣. Plus sérieusement, c'est comme ça depuis la première installation et ça m'énerve d'être trop une buse pour essayer de comprendre pourquoi (plus que le non-affichage).

 

Tentative  de suivre le guide que tu m'as envoyé pour Github dans la matinée, je viendrai éditer le post après avoir tenté une réinstallation via la plateforme.

 

 

++ 

  • Like 1
  • Haha 1
Lien à poster
Partager sur d’autres sites

Bravo, super application. C'est la plus complète et relativement simple à metttre en place. 

Je l'ai fait tourner via portainer sur mon serveur. J'ai mouliné un peu avec l'AI pour ajouter les capteurs que j'ai déjà. 

Les capteurs tournent sur esphome, une solution portée par la communauté Home Assistant. Pour se faire, il suffit : 


Créer backend/app/schemas/esphome.py

 

"""
Schémas Pydantic pour l'intégration ESPHome.
NOUVEAU FICHIER — backend/app/schemas/esphome.py
"""
from datetime import datetime
from typing import Optional
from pydantic import BaseModel


class ESPHomePushPayload(BaseModel):
    """Corps JSON envoyé par le capteur ESPHome via http_request."""
    device_id:   str
    temperature: Optional[float] = None
    humidite:    Optional[float] = None
    co2:         Optional[float] = None
    timestamp:   Optional[int]  = None   # Unix timestamp UTC (optionnel)


class ESPHomePushResult(BaseModel):
    status:  str
    id_log:  Optional[int]  = None
    vpd:     Optional[float] = None
    message: Optional[str]  = None


class ESPHomeDeviceCreate(BaseModel):
    """Enregistrement d'un nouveau capteur ESPHome."""
    nom:       str
    device_id: str
    ip_lan:    Optional[str] = None
    id_espace: Optional[int] = None
    notes:     Optional[str] = None



Étape 2 — Créer backend/app/routers/esphome.py


 

"""
Router ESPHome — intégration capteurs ESPHome dans GrowManager.
NOUVEAU FICHIER — backend/app/routers/esphome.py

Architecture :
  - ESPHome pousse ses données via POST /api/capteurs/esphome/push
  - Les capteurs ESPHome sont stockés dans GoveeDevice avec modele="esphome"
  - Les relevés sont dans TemperatureLog avec source="esphome"
    => graphiques et filtres existants déjà compatibles sans toucher au frontend
"""
from datetime import datetime, timezone
from typing import List, Optional

from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session

from app.database import get_db
from app.models.all_models import GoveeDevice, TemperatureLog
from app.schemas.esphome import ESPHomePushPayload, ESPHomePushResult, ESPHomeDeviceCreate
from app.services.govee_poller import compute_vpd, _get_active_culture_id

router = APIRouter(tags=["esphome"])

ESPHOME_MODELE = "esphome"


# ─────────────────────────────────────────────────────────────────────────────
# Helper interne
# ─────────────────────────────────────────────────────────────────────────────

def _get_device_by_esphome_id(device_id: str, db: Session) -> Optional[GoveeDevice]:
    return (
        db.query(GoveeDevice)
        .filter(
            GoveeDevice.device_id == device_id,
            GoveeDevice.modele    == ESPHOME_MODELE,
        )
        .first()
    )


# ─────────────────────────────────────────────────────────────────────────────
# Endpoint principal : réception des données ESPHome
# ─────────────────────────────────────────────────────────────────────────────

@router.post("/api/capteurs/esphome/push", response_model=ESPHomePushResult)
def esphome_push(payload: ESPHomePushPayload, db: Session = Depends(get_db)):
    """
    Reçoit un relevé depuis un capteur ESPHome via HTTP POST.
    Le capteur doit être enregistré au préalable via POST /api/capteurs/esphome/devices.
    """
    device = _get_device_by_esphome_id(payload.device_id, db)
    if not device:
        raise HTTPException(
            status_code=404,
            detail=f"Capteur ESPHome '{payload.device_id}' non enregistré. "
                   f"Créez-le d'abord via POST /api/capteurs/esphome/devices"
        )

    if not device.actif:
        return ESPHomePushResult(status="ignored", message="Capteur inactif — donnée ignorée")

    # Calcul VPD si temp + humidité disponibles
    vpd = None
    if payload.temperature is not None and payload.humidite is not None:
        vpd = compute_vpd(payload.temperature, payload.humidite)

    # Culture active liée à l'espace du capteur
    id_culture = None
    if device.id_espace:
        id_culture = _get_active_culture_id(db, device.id_espace)

    # Horodatage : timestamp ESPHome si fourni, sinon maintenant
    if payload.timestamp:
        date_heure = datetime.fromtimestamp(payload.timestamp, tz=timezone.utc)
    else:
        date_heure = datetime.now(timezone.utc)

    # Insertion dans TemperatureLog avec source="esphome"
    log = TemperatureLog(
        id_device=   device.id_device,
        id_culture=  id_culture,
        id_espace=   device.id_espace,
        date_heure=  date_heure,
        temperature= payload.temperature,
        humidite=    payload.humidite,
        vpd=         vpd,
        source=      "esphome",
    )
    db.add(log)
    db.commit()
    db.refresh(log)

    return ESPHomePushResult(status="ok", id_log=log.id_log, vpd=vpd)


# ─────────────────────────────────────────────────────────────────────────────
# CRUD capteurs ESPHome
# ─────────────────────────────────────────────────────────────────────────────

@router.get("/api/capteurs/esphome/devices")
def list_esphome_devices(db: Session = Depends(get_db)):
    """Liste tous les capteurs ESPHome enregistrés."""
    return (
        db.query(GoveeDevice)
        .filter(GoveeDevice.modele == ESPHOME_MODELE)
        .order_by(GoveeDevice.nom)
        .all()
    )


@router.post("/api/capteurs/esphome/devices", status_code=201)
def create_esphome_device(payload: ESPHomeDeviceCreate, db: Session = Depends(get_db)):
    """
    Enregistre un nouveau capteur ESPHome.
    Le device_id doit correspondre exactement à celui configuré dans le YAML ESPHome.
    """
    existing = _get_device_by_esphome_id(payload.device_id, db)
    if existing:
        raise HTTPException(
            status_code=409,
            detail=f"Un capteur ESPHome avec device_id='{payload.device_id}' existe déjà"
        )

    device = GoveeDevice(
        nom=       payload.nom,
        device_id= payload.device_id,
        modele=    ESPHOME_MODELE,
        ip_lan=    payload.ip_lan,
        id_espace= payload.id_espace,
        actif=     True,
        notes=     payload.notes,
    )
    db.add(device)
    db.commit()
    db.refresh(device)
    return device


@router.delete("/api/capteurs/esphome/devices/{device_id}", status_code=204)
def delete_esphome_device(device_id: int, db: Session = Depends(get_db)):
    """Supprime un capteur ESPHome (les logs associés sont conservés)."""
    device = db.query(GoveeDevice).filter(GoveeDevice.id_device == device_id).first()
    if not device or device.modele != ESPHOME_MODELE:
        raise HTTPException(status_code=404, detail="Capteur ESPHome introuvable")
    db.delete(device)
    db.commit()


Étape 3 — Modifier backend/app/main.py


Ajouter

from app.routers import esphome

Et

app.include_router(esphome.router)


A la suite des autres from.... et app.inclu.... 

Ouvrir http://votregrowmanager:port/docs
Aller à POST /api/capteurs/esphome/devices


Ajouter

 

{
  "nom": "LeNom",
  "device_id": "LeNom",
  "id_espace": 1,
  "notes": "Capteur température/humidité ESPHome"
}

 

 

 

Ensuite dans esphome, ajouter à votre yaml :

 

interval:
  - interval: 60s
    then:
      - if:
          condition:
            lambda: |-
              return !isnan(id(LeNom_temperature).state) &&
                     !isnan(id(LeNom_humidite).state);
          then:
            - http_request.post:
                url: "http://localhost:port/api/capteurs/esphome/push"
                request_headers:
                  Content-Type: application/json
                json: |-
                  root["device_id"] = "LeNom";
                  root["temperature"] = id(LeNom_temperature).state;
                  root["humidite"] = id(LeNom_humidite).state;
                  root["co2"] = 0;


Petite modification de niche, mais ça pourra peut être guider ceux qui veulent modifier un peu... 

J'ai aussi fait une petite modification perso du docker-compose.yaml

 

version: "3.8"

services:
  db:
    image: mysql:8.2
    container_name: growmanager-db
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-growroot}
      MYSQL_DATABASE: ${MYSQL_DATABASE:-growmanager}
      MYSQL_USER: ${MYSQL_USER:-grow}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD:-grow2024}
      TZ: ${TZ:-Europe/Paris}
    volumes:
      - ${GM_DATA_ROOT}/mysql:/var/lib/mysql
      - ./backend/init.sql:/docker-entrypoint-initdb.d/01_init.sql:ro
    ports:
      - "${MYSQL_PORT:-3306}:3306"
    healthcheck:
      test: ["CMD-SHELL", "mysqladmin ping -h localhost -u${MYSQL_USER:-grow} -p${MYSQL_PASSWORD:-grow2024} --silent"]
      interval: 10s
      timeout: 5s
      retries: 10

  backend:
    build:
      context: ./backend
    container_name: growmanager-backend
    restart: unless-stopped
    environment:
      DATABASE_URL: mysql+pymysql://${MYSQL_USER:-grow}:${MYSQL_PASSWORD:-grow2024}@db:3306/${MYSQL_DATABASE:-growmanager}
      SECRET_KEY: ${SECRET_KEY:-changeme-in-production}
      GROWMANAGER_URL: ${GROWMANAGER_URL:-http://growmanager}
      TZ: ${TZ:-Europe/Paris}
    depends_on:
      db:
        condition: service_healthy
    ports:
      - "${BACKEND_PORT:-8000}:8000"
    volumes:
      - ${GM_DATA_ROOT}/uploads:/app/uploads

  frontend:
    build:
      context: ./frontend
    container_name: growmanager-frontend
    restart: unless-stopped
    depends_on:
      - backend
    ports:
      - "${FRONTEND_PORT:-5173}:5173"

  nginx:
    image: nginx:alpine
    container_name: growmanager-nginx
    restart: unless-stopped
    ports:
      - "${APP_PORT:-80}:80"
    volumes:
      - ${GM_DATA_ROOT}/nginx.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - frontend
      - backend



Avec en environnement

 

GM_DATA_ROOT=/mnt/user/appdata/growmanager
MYSQL_ROOT_PASSWORD=ChangeMoiRoot
MYSQL_DATABASE=growmanager
MYSQL_USER=grow
MYSQL_PASSWORD=ChangeMoiApp
SECRET_KEY=UneCleLongueAleatoire
GROWMANAGER_URL=http://xxx.xxx.xxx.:420
TZ=Europe/Paris
MYSQL_PORT=3306
BACKEND_PORT=8000
FRONTEND_PORT=5173
APP_PORT=420


En changeant les ports dans l'environnement, ça ignore les ports du compose, ça m'arrange. Plus simple de les gérer ici directement. 




Petite question au passage, comment modifier les offsets pour la température des feuilles dans le calcul du vpd ? Serait il plus simple de tourner directement avec une image ? Comme ça on a un versioning plus logique avec des tags etc... Encore une fois, je ne suis qu'un amateur, je dis peut être des conneries... 


A bientôt !


Edit : Je dois être nul mais si l'on crée une culture (alors déjà en cours) comment éditer les différentes dates germination, passage 12/12 etc ? 
Edit2 : On ne peut pas cliquer sur les cases du calendrier passées, mais on peut cliquer sur une à venir et dans le menu déroulant choisir une date passée. Je pense que c'est un bon moyen d'assurer une erreur éventuelle de sélection de date, pas la peine de modifier pour pouvoir cliquer sur les cases passées bien que ça puisse être rébarbatif

Modifié par Dominiquesuppo
Ajoute question culture.
  • Like 1
  • Thanks 2
Lien à poster
Partager sur d’autres sites

Bonsoir les jardigeek

 

Deja un grand merci à tous pour vos retours c'est top !!

 

@Maledikchaouch : La partie Croisements a été revue en profondeur. On a ajouté un troisième onglet dédié "Open Field" qui gère exactement ce que tu décrivais : tu peux créer un projet avec N plantes mères, ajouter autant de pères que tu veux (depuis le stock de pollen ou en phénotype libre), et cocher les pères probables par mère. Pour chaque mère tu indiques si la pollinisation est simple (1 mâle identifié) ou ouverte (plusieurs pères, voire inconnus). À la récolte, ça crée automatiquement une nouvelle variété (♀ × ♂) et un pack de graines dans le catalogue. Et oui, tu peux lier tout ça à une culture active existante pour choisir les parents directement depuis tes plantes en cours.

 

@Dominiquesuppo : On a traité tous tes points :

  • ESPHome est maintenant intégré nativement. Les capteurs ESPHome s'ajoutent dans Paramétrage → onglet Capteurs, côte à côte avec les Govee. Les logs rentrent dans la même table que les autres capteurs, donc les graphiques et le VPD fonctionnent sans rien changer.
  • Offset température foliaire (VPD) : c'est maintenant un paramètre configurable dans Paramétrage → onglet Capteurs. La valeur par défaut est 2°C (différentiel feuille/air classique) mais tu peux l'ajuster.
  • Édition des dates d'une culture déjà en cours : un bouton "Dates" a été ajouté dans le header du détail de culture. Tu peux modifier germination, passage 12/12, début floraison, récolte estimée, date de fin — sans contrainte sur les dates passées.
  • Images Docker avec versioning : c'est fait. Les images sont buildées et publiées automatiquement sur ghcr.io/mdf73/ à chaque tag vX.Y.Z. Pour Portainer, tu utilises docker-compose.prod.yml et un simple ./update.sh v1.2.0 pour passer à une nouvelle version. Plus besoin de builder en local.

 

Merci encore pour les retours constructifs, c'est exactement ce qui fait avancer le projet ! 🙏

Modifié par mdf73
  • Like 4
  • Thanks 1
Lien à poster
Partager sur d’autres sites

Hello !

 

Une nouvelle fois merci !

 

Derniere version installée et tout y est, excepté le Dashboard qui n’a apparemment pas envie que je sache à quoi il ressemble 😅

 Je ferai un petit tour dans les nouvelles fonctionnalités après ma journée. 
 

++

Lien à poster
Partager sur d’autres sites
Il y a 7 heures, Maledikchaouch a dit:

Hello !

 

Une nouvelle fois merci !

 

Derniere version installée et tout y est, excepté le Dashboard qui n’a apparemment pas envie que je sache à quoi il ressemble 😅

 Je ferai un petit tour dans les nouvelles fonctionnalités après ma journée. 
 

++


Salut, 


C'est curieux, les autres pages fonctionnent je suppose ? 

Tu peux depuis ton navigateur faire

F12 → onglet "Console" ou "Réseau/Network" puis recharger la page dashboard.

Tu verras surement une erreur en rouge,  l'endpoint qui plante (ex: GET /api/cultures 500 ou GET /api/stats 404). 

Un petit screen ou copier coller de tout ça, on en saura plus. 

Sinon, un petit terminal dans l'appli docker depuis le dossier du projet 



docker compose logs backend --tail=50


Je présume qu'il y a un terminal, j'ai jamais utilisé docker desktop... 


A bientôt !

  • Like 2
Lien à poster
Partager sur d’autres sites

Yop,

 

Il y a 3 heures, Dominiquesuppo a dit:

C'est curieux, les autres pages fonctionnent je suppose ? 

Yep, et sans soucis. Depuis la première install, je n'ai pas le Dashboard. Tout viré en début de semaine, y compris Docker pour repartir de zéro, mais topujours la même chose. Je suis plus doué avec un marteau qu'un clavier 😂

 

Il y a 3 heures, Dominiquesuppo a dit:

Sinon, un petit terminal dans l'appli docker depuis le dossier du projet 

 

 

Révélation

 

PS C:\Users\pc\OneDrive\Documents\growmanager-main> docker compose logs backend --tail=50
time="2026-06-04T18:48:00+02:00" level=warning msg="C:\\Users\\pc\\OneDrive\\Documents\\growmanager-main\\docker-compose.yml
: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion"
backend-1  |            ^^^^^^^^^^^^^^^^^^^^^^^
backend-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 2236, in _execute_internal   
backend-1  |     result: Result[Any] = compile_state_cls.orm_execute_statement(
backend-1  |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
backend-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/context.py", line 293, in orm_execute_statement
backend-1  |     result = conn.execute(
backend-1  |              ^^^^^^^^^^^^^
backend-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1418, in execute
backend-1  |     return meth(
backend-1  |            ^^^^^
backend-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/sql/elements.py", line 515, in _execute_on_connectio
n
backend-1  |     return connection._execute_clauseelement(
backend-1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
backend-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1640, in _execute_clauseelemen
t
backend-1  |     ret = self._execute_context(
backend-1  |           ^^^^^^^^^^^^^^^^^^^^^^
backend-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1846, in _execute_context
backend-1  |     return self._exec_single_context(
backend-1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^
backend-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1986, in _exec_single_context 
backend-1  |     self._handle_dbapi_exception(
backend-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 2353, in _handle_dbapi_excepti
on
backend-1  |     raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
backend-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1967, in _exec_single_context 
backend-1  |     self.dialect.do_execute(
backend-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/default.py", line 924, in do_execute
backend-1  |     cursor.execute(statement, parameters)
backend-1  |   File "/usr/local/lib/python3.11/site-packages/pymysql/cursors.py", line 153, in execute
backend-1  |     result = self._query(query)
backend-1  |              ^^^^^^^^^^^^^^^^^^
backend-1  |   File "/usr/local/lib/python3.11/site-packages/pymysql/cursors.py", line 322, in _query
backend-1  |     conn.query(q)
backend-1  |   File "/usr/local/lib/python3.11/site-packages/pymysql/connections.py", line 563, in query
backend-1  |     self._affected_rows = self._read_query_result(unbuffered=unbuffered)
backend-1  |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
backend-1  |   File "/usr/local/lib/python3.11/site-packages/pymysql/connections.py", line 825, in _read_query_result
backend-1  |     result.read()
backend-1  |   File "/usr/local/lib/python3.11/site-packages/pymysql/connections.py", line 1199, in read
backend-1  |     first_packet = self.connection._read_packet()
backend-1  |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
backend-1  |   File "/usr/local/lib/python3.11/site-packages/pymysql/connections.py", line 775, in _read_packet
backend-1  |     packet.raise_for_error()
backend-1  |   File "/usr/local/lib/python3.11/site-packages/pymysql/protocol.py", line 219, in raise_for_error
backend-1  |     err.raise_mysql_exception(self._data)
backend-1  |   File "/usr/local/lib/python3.11/site-packages/pymysql/err.py", line 150, in raise_mysql_exception
backend-1  |     raise errorclass(errno, errval)
backend-1  | sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (1054, "Unknown column 'Stock.substrat_type' in
 'field list'")
backend-1  | [SQL: SELECT `Stock`.id_stock AS `Stock_id_stock`, `Stock`.id_variete AS `Stock_id_variete`, `Stock`.id_bocal A
S `Stock_id_bocal`, `Stock`.id_materiel_bocal AS `Stock_id_materiel_bocal`, `Stock`.id_plant AS `Stock_id_plant`, `Stock`.ty
pe_stock AS `Stock_type_stock`, `Stock`.sous_type_stock AS `Stock_sous_type_stock`, `Stock`.lampe_type AS `Stock_lampe_type`
, `Stock`.engrais_type AS `Stock_engrais_type`, `Stock`.substrat_type AS `Stock_substrat_type`, `Stock`.maillage AS `Stock_m
aillage`, `Stock`.type_hash AS `Stock_type_hash`, `Stock`.type_rosin AS `Stock_type_rosin`, `Stock`.date_stock AS `Stock_dat
e_stock`, `Stock`.date_fin_stock AS `Stock_date_fin_stock`, `Stock`.quantite_stock AS `Stock_quantite_stock`, `Stock`.quanti
te_initiale AS `Stock_quantite_initiale`
backend-1  | FROM `Stock`]
backend-1  | (Background on this error at: https://sqlalche.me/e/20/e3q8)

What's next:
    Filter, search, and stream logs from all your Compose services
    in one place with Docker Desktop's Logs view. docker-desktop://dashboard/logs

 


Je comprends plus de trucs sur un texte en japonais...

 

 

Merci à mdf et toi pour la tentative de coup de main  !

 

++

Lien à poster
Partager sur d’autres sites

re 

il y a 48 minutes, Maledikchaouch a dit:

Yop,

 

Yep, et sans soucis. Depuis la première install, je n'ai pas le Dashboard. Tout viré en début de semaine, y compris Docker pour repartir de zéro, mais topujours la même chose. Je suis plus doué avec un marteau qu'un clavier 😂

 

 

 

  Masquer le contenu

 

PS C:\Users\pc\OneDrive\Documents\growmanager-main> docker compose logs backend --tail=50
time="2026-06-04T18:48:00+02:00" level=warning msg="C:\\Users\\pc\\OneDrive\\Documents\\growmanager-main\\docker-compose.yml
: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion"
backend-1  |            ^^^^^^^^^^^^^^^^^^^^^^^
backend-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 2236, in _execute_internal   
backend-1  |     result: Result[Any] = compile_state_cls.orm_execute_statement(
backend-1  |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
backend-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/context.py", line 293, in orm_execute_statement
backend-1  |     result = conn.execute(
backend-1  |              ^^^^^^^^^^^^^
backend-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1418, in execute
backend-1  |     return meth(
backend-1  |            ^^^^^
backend-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/sql/elements.py", line 515, in _execute_on_connectio
n
backend-1  |     return connection._execute_clauseelement(
backend-1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
backend-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1640, in _execute_clauseelemen
t
backend-1  |     ret = self._execute_context(
backend-1  |           ^^^^^^^^^^^^^^^^^^^^^^
backend-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1846, in _execute_context
backend-1  |     return self._exec_single_context(
backend-1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^
backend-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1986, in _exec_single_context 
backend-1  |     self._handle_dbapi_exception(
backend-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 2353, in _handle_dbapi_excepti
on
backend-1  |     raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
backend-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1967, in _exec_single_context 
backend-1  |     self.dialect.do_execute(
backend-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/default.py", line 924, in do_execute
backend-1  |     cursor.execute(statement, parameters)
backend-1  |   File "/usr/local/lib/python3.11/site-packages/pymysql/cursors.py", line 153, in execute
backend-1  |     result = self._query(query)
backend-1  |              ^^^^^^^^^^^^^^^^^^
backend-1  |   File "/usr/local/lib/python3.11/site-packages/pymysql/cursors.py", line 322, in _query
backend-1  |     conn.query(q)
backend-1  |   File "/usr/local/lib/python3.11/site-packages/pymysql/connections.py", line 563, in query
backend-1  |     self._affected_rows = self._read_query_result(unbuffered=unbuffered)
backend-1  |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
backend-1  |   File "/usr/local/lib/python3.11/site-packages/pymysql/connections.py", line 825, in _read_query_result
backend-1  |     result.read()
backend-1  |   File "/usr/local/lib/python3.11/site-packages/pymysql/connections.py", line 1199, in read
backend-1  |     first_packet = self.connection._read_packet()
backend-1  |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
backend-1  |   File "/usr/local/lib/python3.11/site-packages/pymysql/connections.py", line 775, in _read_packet
backend-1  |     packet.raise_for_error()
backend-1  |   File "/usr/local/lib/python3.11/site-packages/pymysql/protocol.py", line 219, in raise_for_error
backend-1  |     err.raise_mysql_exception(self._data)
backend-1  |   File "/usr/local/lib/python3.11/site-packages/pymysql/err.py", line 150, in raise_mysql_exception
backend-1  |     raise errorclass(errno, errval)
backend-1  | sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (1054, "Unknown column 'Stock.substrat_type' in
 'field list'")
backend-1  | [SQL: SELECT `Stock`.id_stock AS `Stock_id_stock`, `Stock`.id_variete AS `Stock_id_variete`, `Stock`.id_bocal A
S `Stock_id_bocal`, `Stock`.id_materiel_bocal AS `Stock_id_materiel_bocal`, `Stock`.id_plant AS `Stock_id_plant`, `Stock`.ty
pe_stock AS `Stock_type_stock`, `Stock`.sous_type_stock AS `Stock_sous_type_stock`, `Stock`.lampe_type AS `Stock_lampe_type`
, `Stock`.engrais_type AS `Stock_engrais_type`, `Stock`.substrat_type AS `Stock_substrat_type`, `Stock`.maillage AS `Stock_m
aillage`, `Stock`.type_hash AS `Stock_type_hash`, `Stock`.type_rosin AS `Stock_type_rosin`, `Stock`.date_stock AS `Stock_dat
e_stock`, `Stock`.date_fin_stock AS `Stock_date_fin_stock`, `Stock`.quantite_stock AS `Stock_quantite_stock`, `Stock`.quanti
te_initiale AS `Stock_quantite_initiale`
backend-1  | FROM `Stock`]
backend-1  | (Background on this error at: https://sqlalche.me/e/20/e3q8)

What's next:
    Filter, search, and stream logs from all your Compose services
    in one place with Docker Desktop's Logs view. docker-desktop://dashboard/logs

 


Je comprends plus de trucs sur un texte en japonais...

 

 

Merci à mdf et toi pour la tentative de coup de main  !

 

++

 
Alors, en fait sur le dashboard, il y a une tuile qui affiche les stocks. Il semble qu'il y ait une erreur liée à cette requête. T'as déjà rempli des infos je suppose ? 
Notamment ce qui est dans ton stock (fleur/rosin/...) ? 
Ce qui est bizarre c'est qu'il dit que  la colonne n'existe pas dans la base de donnée... 

J'arrive pas à reproduire l'erreur de mon côté. Je pense que ça vient de quelque part par là. Mdf73 pourra surement t'en dire un peu plus. 

A bientôt. 


 

  • Thanks 2
Lien à poster
Partager sur d’autres sites

Salut les ordinagrowers

 

Un grand merci a toi @Dominiquesuppo pour le support.

@Maledikchaouch j'ai creusé un peu plus loin ton soucis et ca m'a permis de decouvrir un petit soucis de syhchro .
Du coup pour la faire simple j'ai fait une mise a jour qui corrigera ce soucis pour les prochains utilisateurs donc ca c'est bien.
En revanche toi tu as deja créé les bases de données donc il faut que tu tape 2 commandes :

 

docker-compose exec db mysql -u growuser -pgrowpassword growmanager -e "ALTER TABLE Stock ADD COLUMN substrat_type VARCHAR(200) NULL;"

 

Et une fois que ca c'est passé il faut lancer la commande 

docker-compose restart backend

 

Si ca marche toujours pas tu lance la commande

docker-compose up --build

 

pour tout reconstruire

Au plaisir de te lire (on continue le debugg sur didi si ca te va pour pas trop polluer le fil avec ca! :)

 

++

  • Thanks 2
Lien à poster
Partager sur d’autres sites

Merci à vous deux ! Je teste ça pour le petit déjeuner 👌

 

@Dominiquesuppo, sur la bases de données, je ne me suis occupé que des génétiques et la création de deux espaces de cultures pour faites des essais sur le fonctionnement. Et deux trois trucs pour les amendements je crois. 
 

 

@mdf73, c’est du chinois mais je tente d’appliquer et reviendrai t’embêter en dm si je n’y arrive pas. 
 

 

Encore merci les gars🫶🫶

 

++

Lien à poster
Partager sur d’autres sites

Hello.

 

Bon c'est pas tout ca mais on a une app a présenter. On enchaine donc avec la partie suivante
 

8 - Espaces de culture - Gérez vos boxes et tentes

Ce que c'est
La page Espaces de culture c'est le registre de tous vos espaces de pousse - tentes, box, armoires, salles dédiées. Chaque espace est configuré une fois et devient ensuite disponible partout dans l'application : pour déclarer une culture, pour le suivi des capteurs, pour le calcul du nombre de pots...
920739726_08-EspacesdeCulture-Detail.thumb.png.4af73b01f7313880c74691c73bbc40e8.png

 

Ce qu'on renseigne pour chaque espace
Pour chaque espace vous avez :

  • Le nom (Box 1, Tente veg, Salle de fleur...)
  • Les dimensions en cm (largeur, profondeur, hauteur) - la surface en m² se calcule automatiquement
  • Le statut : Actif, Inactif ou Maintenance
  • Le matériel assigné - lampes, ventilation, irrigation - pioché depuis votre inventaire matériel
  • Des notes libres

1263230213_08-EspacesdeCulture-NouvelEspace-Information.png.3ed2494086ad5c37c4ee2f76e7fc75a1.png1217207270_08-EspacesdeCulture-NouvelEspace-Materiel.png.f471572cdddda5b4d6e11a23c988d60c.png

 

Import / Export
Les espaces sont exportables et importables - pratique si vous changez de machine ou si vous voulez partager votre configuration.
1506572547_08-Espacesdeculture(2).thumb.png.4936324b93f8d762080fda76d296fb67.png

 

++

Lien à poster
Partager sur d’autres sites

9 - Le Matériel - Votre inventaire d'équipements

Ce que c'est
La page Matériel c'est le registre de tout ce que vous avez acheté pour cultiver - lampes, ventilation, irrigation, pots, presses, sacs de filtration... Chaque pièce de matériel est référencée avec sa date d'achat, son prix et ses caractéristiques. Utile pour suivre l'âge de vos équipements et avoir une idée du capital investi.

271433788_09-Materiel-Liste.thumb.png.f589d86fa9b4c6ee7bfd325107f62b6b.png

 

Deux vues disponibles
Vue groupée par catégorie
La vue par défaut - le matériel est regroupé par type : Lampes, Ventilation, Irrigation, Pots, etc. Pratique pour avoir une vue d'ensemble rapide de ce que vous avez dans chaque catégorie.

Vue liste
Toutes les pièces de matériel dans un seul tableau trié et filtrable. Les colonnes sont cliquables pour trier : numéro, nom, marque, code ou numéro de série, date d'achat, prix, âge, état. Pratique quand vous cherchez une pièce précise ou que vous voulez voir tout trié par date d'achat ou par prix.
 

Ajouter un équipement
Pour chaque pièce de matériel vous renseignez :

  • Le nom et la marque
  • La catégorie (depuis la liste configurée dans le Paramétrage)
  • Le code ou numéro de série si vous l'avez
  • La date d'achat et le prix
  • L'état : Neuf, Bon état, Usé, Hors service
  • Les caractéristiques libres - puissance pour une lampe, débit pour une pompe, volume pour un pot...
  • L'âge se calcule automatiquement à partir de la date d'achat.

368908101_09-Material-NouveauMateriel3.png.f23a5f97c6ad6bb82ddae0a467b268f9.png950000275_09-Material-NouveauMateriel2.png.6dcd64ceede5725a816bd8e09e61fdf2.png

 

Le lien avec les Espaces de culture
Le matériel référencé ici est ensuite assignable à vos espaces de culture - vous pouvez indiquer quelle lampe tourne dans quelle box, quel système d'irrigation est branché où. Ça permet aussi de calculer les grammes par watt sur vos récoltes.

 

Gestion des vaporisateurs

Un page de gestion des vaporisateurs est également présente et sera utile lorsque le module de gestion de la consommation sera finalisé.

957532809_09-Materiel-Vaporisateurs.thumb.png.61306c02faf9b2050c37c4c4644ed319.png

 

940118700_09-Materiel-Vaporisateurs-Modifier.thumb.png.73f5787bf39500e65145610dce66378e.png

 

++

Lien à poster
Partager sur d’autres sites

11 - Les Statistiques - Vue globale sur votre activité

Ce que c'est
La page Statistiques c'est la vue macro sur tout ce que vous avez fait depuis que vous utilisez GrowManager. Valeur de votre stock de graines, coûts de culture, production par variété, rendements... Plus vous cultivez et enregistrez, plus cette page devient intéressante.

 

Valeur du stock de graines
Un aperçu de la valeur totale de vos graines en stock, calculée à partir des prix d'achat renseignés dans vos packs. Utile pour avoir une idée de ce que représente votre génothèque en termes d'investissement.

1061646488_11-Statistiques1.thumb.png.688cfe145f44dfb7a2647fe1847340d5.png

 

Coûts de culture
La section que j'utilise le plus pour progresser. Elle affiche six indicateurs calculés sur l'ensemble de vos cultures clôturées :

  • Coût total moyen, maximum et minimum par culture
  • Coût par gramme moyen, maximum et minimum

Ces chiffres sont calculés à la clôture de chaque culture et permettent de voir concrètement si vous optimisez vos coûts d'une culture à l'autre - engrais, substrat, électricité...
Cette section n'apparaît que si vous avez des cultures clôturées avec des données de coût renseignées.
515157523_11-Statistiques2.thumb.png.6adb41b48cc56615e5f71c14a95b9606.png

 

Tendances d'extractions
L'évolution de votre production dans le temps - quantités récoltées par extractions, ratio moyen, .... 

2108223409_11-Statistiques3.thumb.png.fa5cfdd826da88ceae61cd4c640fe7cf.png

 

La page Statistiques se nourrit de toutes les autres pages de l'application - plus vous renseignez vos actions, vos coûts et vos récoltes, plus les stats sont précises et utiles.

 

++

  • Like 1
Lien à poster
Partager sur d’autres sites

12 - Le Paramétrage - Configurez l'application à votre façon

Ce que c'est
La page Paramétrage c'est le panneau de configuration de GrowManager. C'est ici que vous personnalisez toutes les listes déroulantes de l'application - les types de pots, les matières, les catégories de matériel, les types de bocaux... Et c'est aussi ici que vous configurez vos capteurs Govee.
C'est une page qu'on visite surtout au début, pour tout mettre à son goût, et de temps en temps quand on veut ajouter une nouvelle option quelque part.


image.thumb.png.ba7cb41a37357c9afcfb9d7d936b6311.png

 

Les listes configurables
Toutes les listes déroulantes de l'application sont gérées ici. Parmi les listes disponibles :

  • Types et matières de pots
  • Catégories de matériel
  • Types de bocaux
  • Types de substrats
  • Types d'extractions
  • Et d'autres encore...

Pour chaque liste vous pouvez ajouter, modifier ou supprimer des valeurs. Si vous utilisez un type de pot ou un bocal qui n'est pas dans la liste par défaut, c'est ici que vous l'ajoutez - il sera ensuite disponible partout dans l'application.
L'application est livrée avec des valeurs par défaut pour toutes ces listes, donc ça tourne directement à l'installation sans avoir à tout configurer.

 

15049708_12-Paramtrage-General2.thumb.png.07c40126c5ea8acdf220f69ff13c5259.png952646942_12-Paramtrage-General4.thumb.png.48e65beb44eba0232f80841e105b2f84.png1324077613_12-Paramtrage-General5.png.8e525da0903428f27f7e3d3a7338ecec.png292513359_12-Paramtrage-General1.png.560a7c8c33c2a81f27b3c53727cbfca9.png

 

Configuration des capteurs Govee (et ESPHome)
C'est aussi dans le Paramétrage que vous ajoutez vos capteurs Govee. Vous renseignez l'adresse MAC du capteur et son nom - l'application commence ensuite à le poller automatiquement en arrière-plan pour récupérer les relevés de température et d'humidité.
Une fois configuré, le capteur apparaît dans le Dashboard et dans la page Suivi des constantes.

Il est également possible de configurer ici l'offset foliaire. C'est la température qui sera prise en compte pour le calcul du VPD. Pour etre au plus proche de la vérité, le VPD doit etre calculé depuis la température des feuilles et non celle de l'air ambiant. Pour ce faire il suffit de définir cet offset qui est en moyenne de 2°c (ecart entre temp de l'air et des feuilles)


image.png.060618947305ed4aad984a435205b892.png

528580465_12-Paramtrage-Capteurs.thumb.png.66a49635a11c5f3aae496f722a6e1759.png

 

Sauvegarde et restauration

Depuis cette page il est possible de sauvegarder la base de donnée SQL complete ou de l'importer depuis une sauvegarde deja réalisée

image.png.29582b6a7c3d26e5f3040b7655a4a1f5.png

 

L'alerte Stock

Paramétrez ici les seuils d'alertes de stocks bas en fonction du type de stock

image.png.48d5c5b8a60b8ee5cbbb20f8713bb11f.png

 

Je reviens vite pour terminer les 2 dernieres grosse parties : La gestion de la culture et le paramétrage des substrats.

A tout vite

 

Modifié par mdf73
  • Like 1
Lien à poster
Partager sur d’autres sites