Horloge à mots - tuto

Bonjour,

je me permets de vous partager un projet d’horloge à mots - wordclock sur la base d’un ESP32.

Historique :

Je suis parti de l’excellent tutoriel d’installation du projet qui consiste à créer une horloge de mot pour un ESP8266 avec une matrice led ws2812b et un capteur de lumière temt6000 :

Avec les éléments imprimables en 3D et récupérable ici :

J’ai suivi ce tutoriel et j’ai fait ma première horloge à mots.

Ensuite je me suis dit, comme in y a une interface web, c’est surement compatible avec home assistant, j’ai donc essayé d’intégrer cette interface web avec les commande REST. Malheureusement, l’ESP8266 ne présente pas assez de mémoire pour traiter la charge envoyé par home assistant et le fait bugguer. La seulement commande qui avait une petite charge compatible était la commande reboot.

Face à ce constat, je me suis dit, il est surement possible de convertir ce projet vers un esp32.

Et je me suis lancé, à l’aide des ia car sincèrement incapable de la faire de moi même, j’apprends donc le codage avec ces nouveaux outils.

Donc voici le tuto pour créer sa propre horloge à mots sur la base d’un ESP32 sans capteur de luminosité :

Le matériel :

  • un ESP32 de type : ESP32-DevKitC carte de développement de la carte ESP32 CP2102 ESP32-WROOM-32D ESP32-WROOM-32U WIFI + Bluetooth IoT NodeMCU-32,
  • des câble dupont,
  • un dalle led WS2812B (version WS2812B Eco, 16x16),
  • un cable usb/micro usb A
  • un convertisseur 220V/usb type A
  • pour ma part j’ai utilisé uniquement cette grille.stl ( https://www.printables.com/model/425721-word-clock-french/files#preview.file.eqqJP ) et j’ai fait imprimer par une société qui imprime les éléments 3D pour environ 50€.
  • pour les cotés et fond j’ai utilisé de l’isorel de 3mm que j’ai découpé,
  • pour le devant j’ai utilisé une plaque en plastique blanc ABS de 0,5 mm (j’ai essayé plus épais mais le rendu était très mauvais) : 2Pcs 100*100mm-400*500mm Weiß ABS Kunststoff Bord Blatt Dicke 0,5/1/1,5/2/3/4/5/6mm Für CNC Und DIY Modell Teil Zubehör - AliExpress 15 {« order »%3A"3"%2C"eval"%3A"1"%2C"fromPage"%3A"search"}&pdp_npi=6%40dis!EUR!21.21!17.39!!!172.58!141.50!%402103890917606945509431883e3c13!12000047890251995!sea!FR!0!ABX!1!0!n_tag%3A-29910%3Bd%3A55466213%3Bm03_new_user%3A-29895&curPageLogUid=593IyhicAHRO&utparam-url=scene%3Asearch|query_from%3A|x_object_id%3A1005009094824033|_p_origin_prod%3A
  • un plaque de plexiglass 5 mm à mettre au dessus de la plaque en plastique blanc ABS.

Le montage / câblage :

Là je n’ai rien inventé de nouveau, j’ai fait le même montage que ce qui est décrit dans le github initial à la différence que je n’ai pas besoin d’une alimentation externe, le simple fait de branché l’ESP32 avec l’usb, cela permet d’alimenter l’ESP et la dalle led.

J’ai donc branché :

  • la cable rouge de la dalle sur le 5V de l’esp;
  • le cable blanc/noir sur le gnd de l’esp;
  • le cable vert avec une resistance 470 Ohm entre le Din et le gpio 13 de l’esp (à définir dns config.ino).

L’installation avec Arduino IDE :

A l’heure de l’écriture j’utilise Arduino IDE version 2.3.6

1- Ajouter les ESP32 à Arduino :

Dans un premier temps il faut que Arduino puisse reconnaitre les cartes ESP32 pour téléverser les fichiers du projet.

Il faut donc aller dans :

Fichier / Préférences…

Ajouter la ligne dans url de gestionnaire de cartes supplémentaires :

https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json

Redémarrer Arduino.

2- Ajouter les bibliothèques nécessaires à Arduino IDE:

Il est nécessaire d’installer des bibliothèques en passant par : Outils/Gérer les bibliothèques

Le bibliothèque nécessaire à installer :

  • PubSubClient
  • ArduinoJson
  • StringSplitter
  • Adafruit NeoPixel
  • WiFiManager
  • NTPClient
  • LittleFS

3- Télécharger le projet :

Une fois installé, il faut ouvrir le projet avec Arduino que vous trouverez dans le lien ci-dessous.

4 - modifier le fichier config.json et mqqt.ino

Vous devez modifier uniquement les XXX relatif au ssid et mot de passe (lignes 7 et 8) situés dans le fichier config.json avec un editeur de texte type Geany ou notepad++.

Fichier config.json :

{
    "hostname": "horloge",
    "timezone": 1,
    "dst": 0,
    "ntp_server": "europe.pool.ntp.org",
    "wifi": {
        "ssid": "XXX",
        "password": "XXX"
    },
    "colors": {
        "hourmin_mode": 1,
        "seconds_mode": 2,
        "seconds_display": "all",
        "hours_on": {
            "r": 255,
            "g": 0,
            "b": 0
        },
        "minutes_on": {
            "r": 0,
            "g": 0,
            "b": 255
        },
        "seconds_on": {
            "r": 0,
            "g": 255,
            "b": 0
        },
        "back": {
            "r": 0,
            "g": 0,
            "b": 0
        },
        "saturation": 255,
        "offset": 270
    },
    "brightness": {
        "automatic": false,
        "night": {
            "hours": 40,
            "minutes": 40,
            "seconds": 0,
            "back": 0
        },
        "day": {
            "hours": 255,
            "minutes": 255,
            "seconds": 180,
            "back": 0
        },
        "daytime": {
            "start": 7,
            "end": 22
        }
    },
    "restart": {
        "day": "0",
        "hour": 3
    }
}


Ici je ne comprends pas mais malgré le renseignement du ssid et mots de passe, lors de la mise en route de l’esp la première fois, ces éléments ne sont pas reconnus et il y a une connexion AP de l’esp. (exemple de mes limites de codage :wink: ).

Fichier mqtt.ino :

Remplacer les XXX pour faire correspondre à votre configuration mqtt de home assistant :

// =====================================================================
// CONFIGURATION DU BROKER MQTT
// =====================================================================
// Les valeurs ci-dessous sont tirées de votre fichier
const char* mqtt_server = "XXX.XXX.X.XX";
const int mqtt_port = 1883;
const char* mqtt_user = "XXXXXXX";
const char* mqtt_password = "XXXXXXX";
const char* mqtt_client_id = "horloge_textuelle";

5- Préparer le téléversement :

Une fois le projet est ouvert (exemple ouvrir à partir de votre dossier de projet le fichier config.ino) et que le fichier config.json modifié, il faut préparer le téléversement sur l’esp32.

5-1- Identifier le port de votre ESP32 branché à votre PC:

Le plus simple est d’utliser le gestionnaire de périphérique. (clic droit sur demarré / gestionnaire de périphérique) dansla section port (COM et LPT), débranché et rebranché votre ESP pour identifier le bon port.

Si votre esp n’est pas reconnu, il faut installer les pilotes ( CP210x USB to UART Bridge VCP Drivers - Silicon Labs )

5-2 - Définir les paramètres dans Outils:

Il faut sélectionner le type de carte ESP 32 :

Définir le port, suivant ce que vous avez trouvé avec l’outil des gestionnaires de périphériques de windows. Pour ma part , c’était le port COM6.

Pour le paramètre Partition Scheme :

Pour les autres paramètres, je vous laisse regarder sur les captures d’écrans.

6- lancer le téléversement :

Il suffit de cliquer sur la flèche noir dans le rond bleu.

Penser à mettre votre ESP en mode ouvert pour le téléversement en appuyant sur Boot et EN lors du premier téléversement, sinon la connexion sera refusée.

Regarder les log sur la fenêtre du bas jusqu’à la fin.

La compilation et le téléversement dure environ 1 à 2 minutes max, sinon il y a des erreurs qui apparaissent.

7- lancer le téléversement des fichiers data avec FSlittle :

Pour les fichiers situées dans le dossier data, il faut utiliser la commande Upload LittleFS disponible dans la fenètre qui s’ouvre en appuyant sur Ctrl+Shift+P.

Cliquer sur cette commande.

Attention, important il ne faut pas que le moniteur série soit ouvert (l’icône ronde en haut à droite), sinon l’upload ne fonctionnera pas).

Voilà normalement, tout est dans l’ESP32.

Connexion de l’ESP à votre wifi :

Débranché et rebranché votre ESP32 à votre PC.

Dans le moniteur série, vous devriez voir l’ESP démarrer et dire qu’il est en mode AP et qu’il faut se connecter à l’adresse : 192.168.4.1.

Pour configurer votre wifi, il faut connecter votre PC au wifi généré par l’ESP nommé horloge.

un fois connecté à ce wifi, vous ouvrez la page 192.18.4.1 depuis votre navigateur et vous cliquer sur wifi pour entrer votre ssid et votre mot de passe. Vous validez et l’ESP redémarre.

Pensez à vous reconnectez à votre wifi.

Dans le moniteurs séries d’Arduino vous trouverez l’adresse IP attribué à votre ESP32.

Sinon vous aller dans votre gestion d’adresse ip de votre box pour trouver l’ESP qui s’appelle horloge.

Connexion à la page web de l’ESP :

Ouvrez votre navigateur avec l’adresse ip ou avec http://horloge

Vous arrivez sur la page web de l’esp :

Vous pouvez paramétrer les éléments suivants :

  • Général

    Fuseau horaire : UTC+1 pour paris

    Heure d’été (DST) : été ou hiver

  • Couleurs

Mode heures/minutes : les couleurs sont fixes ou dynamiques

Mode secondes : les couleurs sont fixes ou dynamiques

Affichage secondes : si on affiche uniquement la seconde en cours, les secondes précédentes…

  • Palette de couleurs (pour les couleurs en mode fixe)

    Couleur de fond : conseils, la couleurs la plus foncés pour faire ressortir les autres

    Couleur des heures : à votre convenances

    Couleur des minutes : à votre convenances

    Couleur des secondes : à votre convenances

    Couleurs dynamiques

    Saturation : à votre convenances

    Décalage (Hue) : à votre convenances

    Luminosité - Jour

    Réglages diurnes : pour régler la luminosité en journée, souvent plus forte que la nuit

    Fond

    Heures

    Secondes

    Luminosité - Nuit

    Réglages nocturnes : pour régler la luminosité la nuit, souvent moins fortes pour pas éblouir

    Fond

    Heures

    Secondes

    Plage horaire du jour : pour régler le heure du jour et de la nuit. C’est ce qui permet de basculer la luminosité entre jour et nuit.

Vous pouvez décider de laisser les mêmes paramètres jours et nuits.

  • Paramètres avancés

    Nom d’hôte (Hostname) : nom qui s’affichera dans votre box.

    Serveur NTP : europe.pool.ntp.org pour le réglage de l’heure

    Redémarrage automatique : pour resyncrhoniser l’heure

    Système

    Permet d’avoir des informations et un bouton de reset et redémarrage.

Lien avec Home assistant :

Le protocole de communication est mqtt.

Vous devez donc avoir configuré mqtt dans home assistant avec par exemple Mosquitto broker.

1 - Ajout des entités :

Vous devez ajouter les sensors, button, switch, light, select, binary-sensor dans votre fichier configuration.yaml ou dans un fichier horloge.yaml dans packages. Il faudra adapter ici suivant votre configuration et votre choix d’intégration des entités.

Pour ma part j’ai fait le choix qui est discutable après réflexion de scinder mqtt par type d’entités, mais ce n’est certainement pas la méthode la plus judicieuse.

Extrait mon configuration.yaml pour la gestion mqtt :

#MQTT
mqtt:
    sensor: !include mqtt/mqtt.yaml
    button: !include mqtt/button.yaml 
    switch: !include mqtt/switch.yaml
    number: !include mqtt/number.yaml
    select: !include mqtt/select.yaml
    binary_sensor: !include mqtt/binary_sensor.yaml
    light: !include mqtt/light.yaml

Voici les différentes entités par type :

binary_sensor.yaml :

############################################################
##                MQTT HORLOGE TEXTUELLE (BINARY SENSOR)  ##
############################################################

- name: "Horloge - Disponible"
  unique_id: horloge_disponibilite
  state_topic: "horloge/availability"
  payload_on: "online"
  payload_off: "offline"
  device_class: connectivity
  icon: mdi:lan-connect

button.yaml:

############################################################
##                MQTT HORLOGE TEXTUELLE (BUTTON)        ##
############################################################

- name: "Horloge - Redémarrer"
  unique_id: horloge_redemarrer
  command_topic: "horloge/restart/set"
  payload_press: "ON"
  icon: mdi:restart
  
- name: "Horloge - Restaurer Config"
  unique_id: horloge_restaurer_config
  command_topic: "horloge/restore_config/set"
  payload_press: "ON"
  icon: mdi:backup-restore

light.yaml:

############################################################
##           MQTT HORLOGE TEXTUELLE (LIGHTS DÉFINITIF)    ##
############################################################
# Correction de la clé command_off_template manquante.

# === COULEUR DE FOND ===
- name: "Horloge - Couleur de Fond"
  unique_id: horloge_light_back
  schema: template
  state_topic: "horloge/state"
  command_topic: "horloge/colors/back/rgb/set"
  availability_topic: "horloge/availability"
  
  # État (toujours ON)
  state_template: "ON"
  # Commande de couleur (transmet la chaîne rgb(R,G,B))
  command_on_template: "rgb({{ red }},{{ green }},{{ blue }})"
  # CORRECTION CRITIQUE: Cette clé est requise pour schema: template
  command_off_template: "OFF"
  
  # Templates d'état RGB sécurisés
  red_template: >
    {% set colors = value_json.get('colors', {}) %}
    {% set back = colors.get('back', {'r': 255}) %}
    {{ back.get('r', 255) | int(255) }}
  green_template: >
    {% set colors = value_json.get('colors', {}) %}
    {% set back = colors.get('back', {'g': 255}) %}
    {{ back.get('g', 255) | int(255) }}
  blue_template: >
    {% set colors = value_json.get('colors', {}) %}
    {% set back = colors.get('back', {'b': 255}) %}
    {{ back.get('b', 255) | int(255) }}

  icon: mdi:palette

# === COULEUR DES HEURES ===
- name: "Horloge - Couleur des Heures"
  unique_id: horloge_light_hours
  schema: template
  state_topic: "horloge/state"
  command_topic: "horloge/colors/hours/rgb/set"
  availability_topic: "horloge/availability"
  
  state_template: "ON"
  command_on_template: "rgb({{ red }},{{ green }},{{ blue }})"
  command_off_template: "OFF" # <--- AJOUTÉ
  
  red_template: >
    {% set colors = value_json.get('colors', {}) %}
    {% set hours_on = colors.get('hours_on', {'r': 255}) %}
    {{ hours_on.get('r', 255) | int(255) }}
  green_template: >
    {% set colors = value_json.get('colors', {}) %}
    {% set hours_on = colors.get('hours_on', {'g': 0}) %}
    {{ hours_on.get('g', 0) | int(0) }}
  blue_template: >
    {% set colors = value_json.get('colors', {}) %}
    {% set hours_on = colors.get('hours_on', {'b': 0}) %}
    {{ hours_on.get('b', 0) | int(0) }}
  
  icon: mdi:clock-outline

# === COULEUR DES MINUTES ===
- name: "Horloge - Couleur des Minutes"
  unique_id: horloge_light_minutes
  schema: template
  state_topic: "horloge/state"
  command_topic: "horloge/colors/minutes/rgb/set"
  availability_topic: "horloge/availability"
  
  state_template: "ON"
  command_on_template: "rgb({{ red }},{{ green }},{{ blue }})"
  command_off_template: "OFF" # <--- AJOUTÉ
  
  red_template: >
    {% set colors = value_json.get('colors', {}) %}
    {% set minutes_on = colors.get('minutes_on', {'r': 0}) %}
    {{ minutes_on.get('r', 0) | int(0) }}
  green_template: >
    {% set colors = value_json.get('colors', {}) %}
    {% set minutes_on = colors.get('minutes_on', {'g': 0}) %}
    {{ minutes_on.get('g', 0) | int(0) }}
  blue_template: >
    {% set colors = value_json.get('colors', {}) %}
    {% set minutes_on = colors.get('minutes_on', {'b': 255}) %}
    {{ minutes_on.get('b', 255) | int(255) }}
  
  icon: mdi:clock-time-four-outline

# === COULEUR DES SECONDES ===
- name: "Horloge - Couleur des Secondes"
  unique_id: horloge_light_seconds
  schema: template
  state_topic: "horloge/state"
  command_topic: "horloge/colors/seconds/rgb/set"
  availability_topic: "horloge/availability"
  
  state_template: "ON"
  command_on_template: "rgb({{ red }},{{ green }},{{ blue }})"
  command_off_template: "OFF" # <--- AJOUTÉ
  
  red_template: >
    {% set colors = value_json.get('colors', {}) %}
    {% set seconds_on = colors.get('seconds_on', {'r': 0}) %}
    {{ seconds_on.get('r', 0) | int(0) }}
  green_template: >
    {% set colors = value_json.get('colors', {}) %}
    {% set seconds_on = colors.get('seconds_on', {'g': 255}) %}
    {{ seconds_on.get('g', 255) | int(255) }}
  blue_template: >
    {% set colors = value_json.get('colors', {}) %}
    {% set seconds_on = colors.get('seconds_on', {'b': 0}) %}
    {{ seconds_on.get('b', 0) | int(0) }}
  
  icon: mdi:timer-outline

mqtt.yaml ou sensor.yaml :

############################################################
##                MQTT HORLOGE TEXTUELLE (SENSORS)        ##
############################################################
- name: "Horloge - Heure"
  unique_id: horloge_heure
  state_topic: "horloge/time"
  icon: mdi:clock-outline
  
- name: "Horloge - Signal WiFi"
  unique_id: horloge_wifi_signal
  state_topic: "horloge/wifi_signal"
  unit_of_measurement: "dBm"
  device_class: signal_strength
  state_class: measurement
  icon: mdi:wifi
  
- name: "Horloge - État"
  unique_id: horloge_etat_complet
  state_topic: "horloge/state"
  value_template: "{{ value_json.time.hours }}:{{ '%02d' | format(value_json.time.minutes) }}"
  json_attributes_topic: "horloge/state"
  icon: mdi:information-outline
  
- name: "Horloge - Uptime"
  unique_id: horloge_uptime
  state_topic: "horloge/state"
  value_template: "{{ (value_json.uptime / 3600) | round(1) }}"
  unit_of_measurement: "h"
  device_class: duration
  icon: mdi:timer-outline
  
- name: "Horloge - Mémoire Libre"
  unique_id: horloge_memoire_libre
  state_topic: "horloge/state"
  value_template: "{{ (value_json.free_heap / 1024) | round(1) }}"
  unit_of_measurement: "KB"
  icon: mdi:memory

number.yaml:


############################################################
##                MQTT HORLOGE TEXTUELLE (NUMBERS)        ##
############################################################

# === LUMINOSITÉ JOUR ===
- name: "Horloge - Luminosité Jour Heures"
  unique_id: horloge_luminosite_jour_heures
  state_topic: "horloge/brightness/day_hours"
  command_topic: "horloge/brightness/day_hours/set"
  min: 0
  max: 255
  step: 5
  icon: mdi:brightness-6
  
- name: "Horloge - Luminosité Jour Fond"
  unique_id: horloge_luminosite_jour_fond
  state_topic: "horloge/state"
  command_topic: "horloge/brightness/day_back/set"
  value_template: "{{ value_json.brightness.day.back }}"
  min: 0
  max: 255
  step: 5
  icon: mdi:brightness-5
  
- name: "Horloge - Luminosité Jour Secondes"
  unique_id: horloge_luminosite_jour_secondes
  state_topic: "horloge/state"
  command_topic: "horloge/brightness/day_seconds/set"
  value_template: "{{ value_json.brightness.day.seconds }}"
  min: 0
  max: 255
  step: 5
  icon: mdi:brightness-4
  
# === LUMINOSITÉ NUIT ===
- name: "Horloge - Luminosité Nuit Heures"
  unique_id: horloge_luminosite_nuit_heures
  state_topic: "horloge/brightness/night_hours"
  command_topic: "horloge/brightness/night_hours/set"
  min: 0
  max: 255
  step: 5
  icon: mdi:brightness-3
  
- name: "Horloge - Luminosité Nuit Fond"
  unique_id: horloge_luminosite_nuit_fond
  state_topic: "horloge/state"
  command_topic: "horloge/brightness/night_back/set"
  value_template: "{{ value_json.brightness.night.back }}"
  min: 0
  max: 255
  step: 5
  icon: mdi:brightness-2
  
- name: "Horloge - Luminosité Nuit Secondes"
  unique_id: horloge_luminosite_nuit_secondes
  state_topic: "horloge/state"
  command_topic: "horloge/brightness/night_seconds/set"
  value_template: "{{ value_json.brightness.night.seconds }}"
  min: 0
  max: 255
  step: 5
  icon: mdi:brightness-1
  
# === PLAGE HORAIRE ===
- name: "Horloge - Heure Début Jour"
  unique_id: horloge_heure_debut_jour
  state_topic: "horloge/state"
  command_topic: "horloge/brightness/daytime_start/set"
  value_template: "{{ value_json.brightness.daytime.start }}"
  min: 0
  max: 23
  step: 1
  icon: mdi:weather-sunset-up
  
- name: "Horloge - Heure Fin Jour"
  unique_id: horloge_heure_fin_jour
  state_topic: "horloge/state"
  command_topic: "horloge/brightness/daytime_end/set"
  value_template: "{{ value_json.brightness.daytime.end }}"
  min: 0
  max: 23
  step: 1
  icon: mdi:weather-sunset-down
  
# === COULEURS DYNAMIQUES ===
# CORRECTION CRITIQUE: Ajout du filtre | int pour forcer la conversion en nombre
- name: "Horloge - Saturation Couleurs"
  unique_id: horloge_saturation_couleurs
  state_topic: "horloge/state"
  command_topic: "horloge/colors/saturation/set"
  value_template: "{{ value_json.colors.saturation | int(0) }}"
  min: 0
  max: 255
  step: 5
  icon: mdi:palette
  
- name: "Horloge - Décalage Couleurs"
  unique_id: horloge_decalage_couleurs
  state_topic: "horloge/state"
  command_topic: "horloge/colors/offset/set"
  value_template: "{{ value_json.colors.offset | int(0) }}"
  min: 0
  max: 360
  step: 5
  unit_of_measurement: "°"
  icon: mdi:palette-advanced
  
# === FUSEAU HORAIRE ===
- name: "Horloge - Fuseau Horaire"
  unique_id: horloge_fuseau_horaire
  state_topic: "horloge/state"
  command_topic: "horloge/timezone/set"
  value_template: "{{ value_json.timezone | int(0) }}"
  min: -12
  max: 12
  step: 1
  icon: mdi:earth

# ============================================================================
# === COULEURS RGB FIXES ===
# ============================================================================

# --- COULEUR DE FOND (BACK) ---
- name: "Horloge - Fond Rouge"
  unique_id: horloge_color_back_r
  state_topic: "horloge/state"
  command_topic: "horloge/colors/back_r/set"
  value_template: "{{ value_json.colors.back.r | int(0) }}"
  min: 0
  max: 255
  step: 1
  icon: mdi:palette
  
- name: "Horloge - Fond Vert"
  unique_id: horloge_color_back_g
  state_topic: "horloge/state"
  command_topic: "horloge/colors/back_g/set"
  value_template: "{{ value_json.colors.back.g | int(0) }}"
  min: 0
  max: 255
  step: 1
  icon: mdi:palette
  
- name: "Horloge - Fond Bleu"
  unique_id: horloge_color_back_b
  state_topic: "horloge/state"
  command_topic: "horloge/colors/back_b/set"
  value_template: "{{ value_json.colors.back.b | int(0) }}"
  min: 0
  max: 255
  step: 1
  icon: mdi:palette

# --- COULEUR DES HEURES (HOURS_ON) ---
- name: "Horloge - Heures Rouge"
  unique_id: horloge_color_hours_r
  state_topic: "horloge/state"
  command_topic: "horloge/colors/hours_r/set"
  value_template: "{{ value_json.colors.hours_on.r | int(0) }}"
  min: 0
  max: 255
  step: 1
  icon: mdi:palette
  
- name: "Horloge - Heures Vert"
  unique_id: horloge_color_hours_g
  state_topic: "horloge/state"
  command_topic: "horloge/colors/hours_g/set"
  value_template: "{{ value_json.colors.hours_on.g | int(0) }}"
  min: 0
  max: 255
  step: 1
  icon: mdi:palette
  
- name: "Horloge - Heures Bleu"
  unique_id: horloge_color_hours_b
  state_topic: "horloge/state"
  command_topic: "horloge/colors/hours_b/set"
  value_template: "{{ value_json.colors.hours_on.b | int(0) }}"
  min: 0
  max: 255
  step: 1
  icon: mdi:palette

# --- COULEUR DES MINUTES (MINUTES_ON) ---
- name: "Horloge - Minutes Rouge"
  unique_id: horloge_color_minutes_r
  state_topic: "horloge/state"
  command_topic: "horloge/colors/minutes_r/set"
  value_template: "{{ value_json.colors.minutes_on.r | int(0) }}"
  min: 0
  max: 255
  step: 1
  icon: mdi:palette
  
- name: "Horloge - Minutes Vert"
  unique_id: horloge_color_minutes_g
  state_topic: "horloge/state"
  command_topic: "horloge/colors/minutes_g/set"
  value_template: "{{ value_json.colors.minutes_on.g | int(0) }}"
  min: 0
  max: 255
  step: 1
  icon: mdi:palette
  
- name: "Horloge - Minutes Bleu"
  unique_id: horloge_color_minutes_b
  state_topic: "horloge/state"
  command_topic: "horloge/colors/minutes_b/set"
  value_template: "{{ value_json.colors.minutes_on.b | int(0) }}"
  min: 0
  max: 255
  step: 1
  icon: mdi:palette

# --- COULEUR DES SECONDES (SECONDS_ON) ---
- name: "Horloge - Secondes Rouge"
  unique_id: horloge_color_seconds_r
  state_topic: "horloge/state"
  command_topic: "horloge/colors/seconds_r/set"
  value_template: "{{ value_json.colors.seconds_on.r | int(0) }}"
  min: 0
  max: 255
  step: 1
  icon: mdi:palette
  
- name: "Horloge - Secondes Vert"
  unique_id: horloge_color_seconds_g
  state_topic: "horloge/state"
  command_topic: "horloge/colors/seconds_g/set"
  value_template: "{{ value_json.colors.seconds_on.g | int(0) }}"
  min: 0
  max: 255
  step: 1
  icon: mdi:palette
  
- name: "Horloge - Secondes Bleu"
  unique_id: horloge_color_seconds_b
  state_topic: "horloge/state"
  command_topic: "horloge/colors/seconds_b/set"
  value_template: "{{ value_json.colors.seconds_on.b | int(0) }}"
  min: 0
  max: 255
  step: 1
  icon: mdi:palette

select.yaml:

############################################################
##                 MQTT HORLOGE TEXTUELLE (SELECT)         ##
############################################################

- name: "Horloge - Mode Couleurs H/M"
  unique_id: horloge_mode_couleurs_hm
  # Cette entité est déjà sécurisée car elle ne lit pas dans un gros JSON
  state_topic: "horloge/colors/hourmin_mode"
  command_topic: "horloge/colors/hourmin_mode/set"
  options:
    - "0"  # Couleurs fixes
    - "1"  # Couleurs dynamiques
  icon: mdi:palette-outline
  
- name: "Horloge - Mode Couleurs Secondes"
  unique_id: horloge_mode_couleurs_secondes
  state_topic: "horloge/state"
  command_topic: "horloge/colors/seconds_mode/set"
  # CORRECTION: Utilisation de .get() pour éviter l'erreur si 'colors' ou 'seconds_mode' n'existe pas
  value_template: "{{ value_json.get('colors', {}).get('seconds_mode', '0') }}"
  options:
    - "0"  # Couleurs fixes
    - "1"  # Dynamiques communes
    - "2"  # Dynamiques uniques
  icon: mdi:palette-swatch
  
- name: "Horloge - Affichage Secondes"
  unique_id: horloge_affichage_secondes
  state_topic: "horloge/state"
  command_topic: "horloge/colors/seconds_display/set"
  # CORRECTION: Utilisation de .get() pour éviter l'erreur si 'colors' ou 'seconds_display' n'existe pas
  value_template: "{{ value_json.get('colors', {}).get('seconds_display', 'all') }}"
  options:
    - "past"  # Secondes passées
    - "one"   # Seconde actuelle
    - "all"   # Toutes sauf actuelle
  icon: mdi:clock-time-four-outline

switch.yaml:

############################################################
##                MQTT HORLOGE TEXTUELLE (SWITCH)         ##
############################################################

    - name: "Horloge - Heure d'Été"
      unique_id: horloge_heure_ete
      state_topic: "horloge/state"
      command_topic: "horloge/dst/set"
      value_template: "{% if value_json.dst == 1 %}ON{% else %}OFF{% endif %}"
      payload_on: "1"
      payload_off: "0"
      icon: mdi:weather-sunny

2 - Card lovelace : (très basique)

type: vertical-stack
cards:
  - type: entities
    title: Statut
    entities:
      - sensor.horloge_heure
      - sensor.horloge_signal_wifi
  - type: entities
    title: Luminosité Jour
    entities:
      - number.horloge_luminosite_jour_heures
      - number.horloge_luminosite_jour_fond
      - number.horloge_luminosite_jour_secondes
  - type: entities
    title: Luminosité Nuit
    entities:
      - number.horloge_luminosite_nuit_heures
      - number.horloge_luminosite_nuit_fond
      - number.horloge_luminosite_nuit_secondes
  - type: entities
    title: Couleurs
    entities:
      - select.horloge_mode_couleurs_h_m
      - select.horloge_mode_couleurs_secondes
      - select.horloge_affichage_secondes
      - number.horloge_saturation_couleurs
      - number.horloge_decalage_couleurs
  - type: entities
    title: Paramètres
    entities:
      - number.horloge_fuseau_horaire
      - switch.horloge_heure_d_ete
  - type: entities
    title: Système
    entities:
      - sensor.horloge_memoire_libre
      - button.horloge_redemarrer
      - button.horloge_restaurer_config
  - type: entities
    title: Couleur de Fond
    entities:
      - entity: light.horloge_couleur_de_fond
        name: Couleur de Fond
        icon: mdi:palette
      - entity: light.horloge_couleur_des_heures
        name: Couleur des Heures
        icon: mdi:clock-outline
      - entity: light.horloge_couleur_des_minutes
        name: Couleur des Minutes
        icon: mdi:clock-time-four-outline
      - entity: light.horloge_couleur_des_secondes
        name: Couleur des Secondes
        icon: mdi:timer-outline

Voilà j’espère que cela vous permettra de réaliser ce beau projet.

PS : je ne comprends pas pourquoi mais les secondes avancenet 2 par 2 alors qu’avec l’esp8622 la led s’allume pour chaque seconde. En soit c’est pas bien grave.

Amusez vous bien!

Le rendu :

5 « J'aime »

Bon, alors…

les fichiers stl, c’est fait
l’esp8266 il rame tellement velu velu qu’il a du mal à booter.
un esp32, c’est tout bon, ca affiche en french.

par contre, pour les fichiers, je butte un peu…

j’ai créé une arborescence « mqtt » dans /config/packages avec les fichiers

 Répertoire de \\packages

31/10/2025  20:39    <DIR>          .
31/10/2025  20:39    <DIR>          ..
31/10/2025  20:50    <DIR>          mqtt
20/08/2025  14:46             2 639 speedtest.yaml
31/10/2025  20:49               312 horloge.yaml
               2 fichier(s)            2 951 octets

 Répertoire de \\packages\mqtt

31/10/2025  20:50    <DIR>          .
31/10/2025  20:39    <DIR>          ..
31/10/2025  19:35               389 binary_sensor.yaml
31/10/2025  20:45               499 button.yaml
31/10/2025  20:49                 0 climate.yaml
31/10/2025  20:45             4 108 light.yaml
31/10/2025  20:46             1 205 mqtt.yaml
31/10/2025  20:46             6 906 number.yaml
31/10/2025  20:47               998 select.yaml
31/10/2025  20:47               453 switch.yaml
               8 fichier(s)           14 558 octets

     Total des fichiers listés :
              10 fichier(s)           17 509 octets

j’ai dabord créé un fichier climate.yaml vide because:

Configuration errors
Error loading /config/configuration.yaml:   in "/config/packages/horloge.yaml", line 5, column 12: Unable to read file /config/packages/mqtt/climate.yaml

bon, il veut un mqtt/climate, je lui met un mqtt/climate

mais lors du check config suivant…

et le configuration.html contient bien

homeassistant:
  # Load packages
  packages: !include_dir_merge_named packages

c’est où que j’ai merdé ?

Salut, merci pour l’intérêt du projet !

Alors en faite pas besoin de climate.yaml dans configuration.yaml.

Tu peux enlever cette ligne et le fichier dans ton dossier mqtt car j’ai pas fait le tri lorsque j’ai partagé l’extrait de mon fichier configuration.yaml.

Mon fichier climate me sert à utiliser bsblan pour piloter ma pompe à chaleur.
Je modifie dans mon tuto, ça prête à confusion.

Pour la partie mqtt :

Voici mon arborescence :
un dossier mqtt dans homeassistant qui contient mes différents fichiers par type.

Et pour chaque fichier yaml je mets le code yaml correspondant.
Par exemple pour le bianry_sensor:

Je pense que de ce que je comprends tu as mis ton dossier dans packages et non directement dans le dossier racine homeassistant ou aussi appelé config.
Si tu veux garder les fichiers dans ton dossier package il faut que tu lui dise où il peut trouver les fichiers yaml, adaptes

mqtt:
    sensor: !include packages/mqtt/mqtt.yaml
    button: !include packages/mqtt/button.yaml 
    switch: !include packages/mqtt/switch.yaml
    number: !include packages/mqtt/number.yaml
    select: !include packages/mqtt/select.yaml
    binary_sensor: !include packages/mqtt/binary_sensor.yaml
    light: !include packages/mqtt/light.yaml

Je pense que ce n’est qu’un problème de localisation de dossier et d’indication à home assistant où trouver les fichiers.

Sinon tu peux tout mettre dans configuration.yaml :
exemple :

mqtt:
   
  binary_sensor:
      
      - name: "Horloge - Disponible"
        unique_id: horloge_disponibilite
        state_topic: "horloge/availability"
        payload_on: "online"
        payload_off: "offline"
        device_class: connectivity
        icon: mdi:lan-connect

  select:
      - name: "Horloge - Mode Couleurs H/M"
        unique_id: horloge_mode_couleurs_hm
        # Cette entité est déjà sécurisée car elle ne lit pas dans un gros JSON
        state_topic: "horloge/colors/hourmin_mode"
        command_topic: "horloge/colors/hourmin_mode/set"
        options:
          - "0"  # Couleurs fixes
          - "1"  # Couleurs dynamiques
        icon: mdi:palette-outline
        
      - name: "Horloge - Mode Couleurs Secondes"
        unique_id: horloge_mode_couleurs_secondes
        state_topic: "horloge/state"
        command_topic: "horloge/colors/seconds_mode/set"
        # CORRECTION: Utilisation de .get() pour éviter l'erreur si 'colors' ou 'seconds_mode' n'existe pas
        value_template: "{{ value_json.get('colors', {}).get('seconds_mode', '0') }}"
        options:
          - "0"  # Couleurs fixes
          - "1"  # Dynamiques communes
          - "2"  # Dynamiques uniques
        icon: mdi:palette-swatch
        
      - name: "Horloge - Affichage Secondes"
        unique_id: horloge_affichage_secondes
        state_topic: "horloge/state"
        command_topic: "horloge/colors/seconds_display/set"
        # CORRECTION: Utilisation de .get() pour éviter l'erreur si 'colors' ou 'seconds_display' n'existe pas
        value_template: "{{ value_json.get('colors', {}).get('seconds_display', 'all') }}"
        options:
          - "past"  # Secondes passées
          - "one"   # Seconde actuelle
          - "all"   # Toutes sauf actuelle
        icon: mdi:clock-time-four-outline

#etc...

dis moi si c’est ok

Salut

Vu que tu utilises les packages, tu crées dans ton dossier packages un fichier afficheur.yaml par exemple (tu peux mettre le nom que tu veux).

Dans ce fichier tu mets tous les sensors comme tu les mettrais dans ton configuration.yaml. Voir l’exemple donné par @jpjobe dans son dernier post et ça devrait faire le travail.

1 « J'aime »

bon, finalement, j’ai bien tout mis dans la racine.
ce qui me posait des problèmes c’est que j’avais déjà un paragraphe mqtt dans mon configuration.yaml

mqtt:
  sensor:
    # For iv9clock sensors -----------------------------------------------------
    - state_topic: "clock/neonixie/iv9clock-67ac/lux"
      name: "iv9clock-67ac lux"
      unique_id: "iv9clock-67ac_lux"
      device_class: "illuminance"
      unit_of_measurement: "lx"
    - state_topic: "clock/neonixie/iv9clock-67ac/bmp_PA"
      name: "iv9clock-67ac bmp_PA"
      unique_id: "iv9clock-67ac_bmp_PA"
      device_class: "atmospheric_pressure"
      unit_of_measurement: "Pa"
    # End iv9clock sensors -----------------------------------------------------

j’ai commenté tout ce qu’il y avait auparavant et il accepte sans insultes mes includes.

faudra que je remette ca plus tard …

enfin presque sans insultes…

il ne me trouve pas

  • select.horloge_affichage_secondes
  • number.horloge_saturation_couleurs_fix
  • number.horloge_decalage_couleurs_fix

faut que j’arrive à piger le fonctionnement des couleurs, j’arrive pas à mettre les heures et les minutes avec des couleurs différentes.
et moi aussi, les secondes sautent 2 par 2…:wink:

ca avance, ca avance !!!

1 « J'aime »

c’est ce que j’avais fait dans
/config/packages/horloge.yaml
il avait bien détecté qu’il me manquait un /config/packages/mqtt/climate.yaml
je l’ai ajouté avec rien dedans et c’est là qu’il m’a sorti la page d’invalid config posté 3 message plus haut…

Non pas vraiment.

Packages/horloge.yaml

Et dans horloge.yaml

mqtt:
   
  binary_sensor:
      
      - name: "Horloge - Disponible"
        unique_id: horloge_disponibilite
        state_topic: "horloge/availability"
        payload_on: "online"
        payload_off: "offline"
        device_class: connectivity
        icon: mdi:lan-connect

  select:
      - name: "Horloge - Mode Couleurs H/M"
        unique_id: horloge_mode_couleurs_hm
        # Cette entité est déjà sécurisée car elle ne lit pas dans un gros JSON
        state_topic: "horloge/colors/hourmin_mode"
        command_topic: "horloge/colors/hourmin_mode/set"
        options:
          - "0"  # Couleurs fixes
          - "1"  # Couleurs dynamiques
        icon: mdi:palette-outline
        
      - name: "Horloge - Mode Couleurs Secondes"
        unique_id: horloge_mode_couleurs_secondes
        state_topic: "horloge/state"
        command_topic: "horloge/colors/seconds_mode/set"
        # CORRECTION: Utilisation de .get() pour éviter l'erreur si 'colors' ou 'seconds_mode' n'existe pas
        value_template: "{{ value_json.get('colors', {}).get('seconds_mode', '0') }}"
        options:
          - "0"  # Couleurs fixes
          - "1"  # Dynamiques communes
          - "2"  # Dynamiques uniques
        icon: mdi:palette-swatch
        
      - name: "Horloge - Affichage Secondes"
        unique_id: horloge_affichage_secondes
        state_topic: "horloge/state"
        command_topic: "horloge/colors/seconds_display/set"
        # CORRECTION: Utilisation de .get() pour éviter l'erreur si 'colors' ou 'seconds_display' n'existe pas
        value_template: "{{ value_json.get('colors', {}).get('seconds_display', 'all') }}"
        options:
          - "past"  # Secondes passées
          - "one"   # Seconde actuelle
          - "all"   # Toutes sauf actuelle
        icon: mdi:clock-time-four-outline

#etc...

Tous les sensors qui se trouvent dans les différents fichiers.

Et aucune lignes à ajouter dans configuration.yaml ni fichiers à ajouter dans packages/mqtt qui n’a aucune raison d’être dans ce cas.

1 « J'aime »

Ah OK il ne prenait pas tous les fichiers dans packages/mqtt/*.html

si je ne parviens pas à fusionner mes 2 arborescences mqtt: je ferai comme çà.

1 « J'aime »

Pour les couleurs il faut passer au light configuré dans light.yaml, les number doivent être des restes de mes premiers essais :confused:.

En cliquant sur couleur on peut choisir dans la palette

Je trouvais cela plus intuitif

D’après claude ai Voici une solution pour les secondes pour qu’elles s’affichent 1 par 1 :

“Pour le problème des secondes qui avancent 2 par 2, je pense savoir d’où ça vient. C’est probablement lié à la condition dans la boucle des secondes :

cppif(i <= seconds && i > 0){ // Mode "past"

Le i <= seconds fait que quand on est à la seconde 10, les LEDs 1 à 10 s’allument, mais comme certaines secondes n’ont pas de LED (0, 30), ça peut créer des « sauts visuels ».

Si jamais vous voulez corriger ça plus tard, essayez de changer :

cppif(i <= seconds && i > 0){

en :

cppif(i < seconds){

Ça doit être dans le fichier config.ino

dis moi @jpjobe il manquerait pas quelques lignes à ton select.yaml ?

1 « J'aime »

Salut,

je te mets le code que j’ai dans mon select.yaml :


############################################################
##                 MQTT HORLOGE TEXTUELLE (SELECT)         ##
############################################################

- name: "Horloge - Mode Couleurs H/M"
  unique_id: horloge_mode_couleurs_hm
  # Cette entité est déjà sécurisée car elle ne lit pas dans un gros JSON
  state_topic: "horloge/colors/hourmin_mode"
  command_topic: "horloge/colors/hourmin_mode/set"
  options:
    - "0"  # Couleurs fixes
    - "1"  # Couleurs dynamiques
  icon: mdi:palette-outline
  
- name: "Horloge - Mode Couleurs Secondes"
  unique_id: horloge_mode_couleurs_secondes
  state_topic: "horloge/state"
  command_topic: "horloge/colors/seconds_mode/set"
  # CORRECTION: Utilisation de .get() pour éviter l'erreur si 'colors' ou 'seconds_mode' n'existe pas
  value_template: "{{ value_json.get('colors', {}).get('seconds_mode', '0') }}"
  options:
    - "0"  # Couleurs fixes
    - "1"  # Dynamiques communes
    - "2"  # Dynamiques uniques
  icon: mdi:palette-swatch
  
- name: "Horloge - Affichage Secondes"
  unique_id: horloge_affichage_secondes
  state_topic: "horloge/state"
  command_topic: "horloge/colors/seconds_display/set"
  # CORRECTION: Utilisation de .get() pour éviter l'erreur si 'colors' ou 'seconds_display' n'existe pas
  value_template: "{{ value_json.get('colors', {}).get('seconds_display', 'all') }}"
  options:
    - "past"  # Secondes passées
    - "one"   # Seconde actuelle
    - "all"   # Toutes sauf actuelle
  icon: mdi:clock-time-four-outline

oui visiblement il y avait pas tout :face_with_peeking_eye:

c’est mieux, j’ai plus de messages d’erreur
J’ai juste renommé les entrées

number.horloge_saturation_couleurs_fix
number.horloge_decalage_couleurs_fix

dans le dashboard.en enlevant le _fix

1 « J'aime »

Merci de ta vigilance, j’ai mis à jour le tuto, tu es mon testeur :+1: