M5stack Dial et ESPHOME

Salut. J’ai attaqué un projet sur cette bestiole : le M5stack Dial. Base ESP32 / écran LCD 240X240 / Buzzer / RFID / couronne rotative cliquable (partie basse)

Y’a un « composant » tout prêt, que j’ai choisi de ne pas utiliser, ni tester d’ailleurs.

En utilisant Esphome y’a moyen d’en faire à peu près ce qu’on veut…
J’ai posté mon avancement sur le forum HA, sur lequel j’ai pu puiser les bases et les fichiers de config de ceux qui avaient déjà bien avancé.

Ça donne ça pour le moment, intégré dans un interrupteur standard (Schneider gamme Odace) :

4 « J'aime »

Félicitations
Jolie boulot
Quel sont les actions possibles avec cette bête ?
Quels sont tes objectifs a terme de réaliser

Ouvrir le portail, allumer une lumière, n’importe quel service home assistant en somme.
Perso ce sera pour remplacer un interrupteur dont ‹ l’autre adulte de la maison › ne se souvient jamais des pattern.
Les icônes changent en fonction de l’état des entités HA, et à terme les actions disponibles aussi.
Mes réflexions et le design associé sont:
Une page d’accueil 4 cadrans. Un click dans un cadran amène une ou plusieurs pages navigables via la molette / couronne. Dans chacune de ces pages un ou plusieurs cadrans à toucher par actions possibles.
Le bouton physique sert toujours à revenir en arrière (ou éteindre).

1 « J'aime »

Salut,
super boulot. Le M5stack Dial est top, mais l’écran est trop petit.
Product Size: 45x45x32.3mm , screen: 1.28 Inch.
Faut bien y voir :wink:

sympa comme tout.
l’écran est un peu petit, mais c’est pas destiné à éditer un bouquin non plus !!!
bien suffisant pour quelques infos.

tu pourrais nous partager les include (m5dial_files/colors_and_fonts.yaml et images.yaml) ?

1 « J'aime »



Si t’es chaud?! :smiley:

Voilà à quoi ressemble la page_b1 par exemple.

Code 19/06/24
wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  #########################
  # use_address: 192.168.2.53
  #########################
  manual_ip:
    static_ip: !secret ip_m5stack_dial
    gateway: !secret wifi_gtw
    subnet: !secret wifi_sub
  ap:
    ssid: "m5stack-dial-fallback"
    password: !secret wifi_ap_password

ota:
  password: !secret ota_password

logger:
  # level: DEBUG

api:
  encryption:
    key: !secret api_encryption_key

esp32:
  board: esp32-s3-devkitc-1
  framework:
    type: arduino

esphome:
  name: ${name}
  friendly_name: ${friendly_name}
  on_boot:
    then:
      - pcf8563.read_time: rtc_time
      - delay: 13s
      - light.turn_on: backlight
      - display.page.show: page_01
      
external_components:
  - source: github://dgaust/esphome@gc9a01
    components: [ gc9a01, ft3267 ]
    refresh: 0s

<<: !include m5dial_files/colors_and_fonts.yaml
<<: !include m5dial_files/images_and_icons.yaml

spi: #required by display.ili9xxx
  mosi_pin: GPIO5
  clk_pin: GPIO6

i2c:
  - id: bus_internal #required by touchscreen.ft3267
    sda: GPIO11
    scl: GPIO12
    scan: false

touchscreen:
  platform: ft3267
  on_touch:
    then:
      - logger.log:
          format: 'Touch ID: %d at _____________________ (%d, %d)'
          args: [touch.id, touch.x, touch.y]

rc522_i2c: # RFiD
  i2c_id:  bus_internal
  address: 0x28
  on_tag:
    then:
      - rtttl.play: 'two_short:d=4,o=5,b=100:16e6,16e6'
      - homeassistant.tag_scanned: !lambda 'return x;'

rtttl: # buzzer
  output: rtttl_out
  id: my_rtttl

time:
  - platform: pcf8563
    id: rtc_time
    address: 0x38
    update_interval: never
  - platform: homeassistant
    on_time_sync:
      then:
        - pcf8563.write_time: rtc_time
        - logger.log: "time synced"

output:
  - id: lcdbacklight # screen backlight
    platform: ledc
    pin: GPIO9
    min_power: 0
    max_power: 1
  - platform: ledc # buzzer
    pin: GPIO3
    id: rtttl_out

substitutions:
  name: tab-m5stack-dial
  friendly_name: M5Stack Dial
  wifi_ssid: !secret wifi_ssid
  wifi_password: !secret wifi_password

  alarm_panel_id: alarm_control_panel.alarme_maison
  climate_id: climate.heatpump_sdgs_ebusd
  door_bell_id: input_boolean.test
  garage_sensor_id: binary_sensor.io_frient_garage_09c6_input_1
  garage_door_sensor_id: binary_sensor.magnet_aqara_garage_door_contact
  gate_sensor_id: binary_sensor.tplt_gate_open_too_long
  lux_sensor_id: sensor.ip030_mmw_aqara_living_luminance
  window_group_id: binary_sensor.windows_upstairs
  light_01_id: light.shelly_one_cour
  window_01_id: binary_sensor.magnet_aqara_bedroom_north_contact
  window_02_id: binary_sensor.magnet_aqara_kitchen_contact
  window_03_id: binary_sensor.magnet_aqara_attic_contact
  window_04_id: binary_sensor.magnet_aqara_bedroom_south_contact
  window_05_id: binary_sensor.magnet_aqara_bay_contact

# ======================================================================================
# ======================================================================================
# ========================================================================== text sensor

text_sensor:
  - platform: homeassistant
    id: alarm_text # armed_away / armed_home / pending etc....
    entity_id: $alarm_panel_id
    on_value:
      then:
        - display.page.show: !lambda |-
            auto selector = id(page_selector).state;
            if (selector == "page_off" or selector == "page_01") {
              return id(page_a1);
            } else {
              return nullptr;
            }
        - component.update: screen
  - platform: homeassistant
    id: gate_text # closed / open / alert
    entity_id: $gate_sensor_id
    attribute: esphome_text_sensor
    <<: &gate_value
      on_value:
        then:
          - display.page.show: !lambda |-
              auto selector = id(page_selector).state;
              auto gate = id(gate_text).state;
              if (selector == "page_off" or selector == "page_01") {
                if (gate == "open" or gate == "alert") {
                  return id(page_b1);
                } else {
                  return nullptr;
                }
              } else {
                return nullptr;
              }
          - component.update: screen
  - platform: homeassistant
    id: garage_text # off / on
    entity_id: $garage_sensor_id
    on_value:
      then:
        - display.page.show: !lambda |-
            auto selector = id(page_selector).state;
            if (selector == "page_off" or selector == "page_01") {
              if (id(garage_text).state == "on") {
                return id(page_b2);
              } else {
                return nullptr;
              }
            } else {
              return nullptr;
            }
        - component.update: screen
  - platform: homeassistant
    id: garage_door_text # off / on
    entity_id: $garage_door_sensor_id
    on_value:
      then:
        - display.page.show: !lambda |-
            auto selector = id(page_selector).state;
            if (selector == "page_off" or selector == "page_01") {
              if (id(garage_door_text).state == "on") {
                return id(page_b2);
              } else {
                return nullptr;
              }
            } else {
              return nullptr;
            }
        - component.update: screen
  - platform: homeassistant
    id: climate_mode_text # auto / heat / off
    entity_id: $climate_id
  - platform: homeassistant
    id: door_bell_text # off / on
    entity_id: $door_bell_id
    <<: *gate_value
  - platform: homeassistant
    id: light_01_text # off / on
    entity_id: $light_01_id
  - platform: homeassistant
    id: window_group_text # off / on
    entity_id: $window_group_id
  - platform: homeassistant
    id: window_01_text # off / on
    entity_id: $window_01_id
  - platform: homeassistant
    id: window_02_text # off / on
    entity_id: $window_02_id
  - platform: homeassistant
    id: window_03_text # off / on
    entity_id: $window_03_id
  - platform: homeassistant
    id: window_04_text # off / on
    entity_id: $window_04_id
  - platform: homeassistant
    id: window_05_text # off / on
    entity_id: $window_05_id

# ======================================================================================
# ======================================================================================
# ================================================================================ light

light:
  - platform: monochromatic
    id: backlight
    name: "Backlight"
    output: lcdbacklight
    default_transition_length: 500ms

# ======================================================================================
# ======================================================================================
# =============================================================================== select

select:
  - platform: template
    name: Page Selector
    icon: "mdi:book-open-page-variant-outline"
    id: page_selector
    options:
     - "page_off"
     - "page_01"
     - "page_02"
     - "page_a1"
     - "page_b1"
     - "page_b2"
     - "page_c1"
     - "page_d1"
    initial_option: "page_01"
    optimistic: true
    on_value:
      - if:
          condition:
            - lambda: 'return id(page_selector).state == "page_off";'
          then:
            - display.page.show: page_off
            - lambda: 'id(lcdbacklight).set_level(0.1);'
            - delay: 8s
            - light.turn_off: backlight
          else:
            - if:
                condition:
                  - light.is_off: backlight
                then:
                  - light.turn_on: backlight
            - display.page.show: !lambda |-
                auto selector = id(page_selector).state;
                if (selector == "page_01") {
                  return id(page_01);
                } else if (selector == "page_02") {
                  return id(page_02);
                } else if (selector == "page_a1") {
                  return id(page_a1);
                } else if (selector == "page_b1") {
                  return id(page_b1);
                } else if (selector == "page_b2") {
                  return id(page_b2);
                } else if (selector == "page_c1") {
                  return id(page_c1);
                } else if (selector == "page_d1") {
                  return id(page_d1);
                } else {
                  return nullptr;
                }
            - component.update: screen

# ======================================================================================
# ======================================================================================
# ====================================================================== display + pages

display:
  - platform: ili9xxx
    model: gc9a01a
    reset_pin: GPIO8
    id: screen
    cs_pin: GPIO7
    dc_pin: GPIO4
    dimensions:
      height: 240
      width: 240
    on_page_change:
      - to: page_off
        then:
          - select.set:
              id: page_selector
              option: page_off
          - component.update: screen
      - to: page_01
        then:
          - select.set:
              id: page_selector
              option: page_01
          - component.update: screen
      - to: page_02
        then:
          - select.set:
              id: page_selector
              option: page_02
          - component.update: screen
      - to: page_a1
        then:
          - select.set:
              id: page_selector
              option: page_a1
          - component.update: screen
      - to: page_b1
        then:
          - select.set:
              id: page_selector
              option: page_b1
          - component.update: screen
      - to: page_b2
        then:
          - select.set:
              id: page_selector
              option: page_b2
          - component.update: screen
      - to: page_c1
        then:
          - select.set:
              id: page_selector
              option: page_c1
          - component.update: screen
      - to: page_d1
        then:
          - select.set:
              id: page_selector
              option: page_d1
          - component.update: screen
    pages:

      # ------------------------------------------------------------ qrcode page
      - id: page_off
        lambda: |-
          // variables =========================================================
          float screenwidth = it.get_width();
          float screenheight = it.get_height();
          float halfscreenwidth = screenwidth /2;
          float halfscreenheight = screenheight / 2;
          // background draws ==================================================
          it.filled_rectangle(0, 0, screenwidth, screenheight, COLOR_ON);
          // content ===========================================================
          it.print(halfscreenwidth, 15, id(roboto_20_bold), blue_color, TextAlign::CENTER, "SSID:");
          it.printf(halfscreenwidth, 35, id(roboto_20_bold), blue_color, TextAlign::CENTER, "${wifi_ssid}");
          it.image(halfscreenwidth, halfscreenheight + 10, img_off, ImageAlign::CENTER);

      # ----------------------------------------------------------- welcome page
      - id: page_01
        lambda: |-
          // variables =========================================================
          float screenheight = it.get_height();
          float screenwidth = it.get_width();
          float halfscreenheight = screenheight / 2;
          float halfscreenwidth = screenwidth /2;
          auto gate = id(gate_text).state;
          auto garage = id(garage_text).state;
          auto garage_door = id(garage_door_text).state;
          auto mode = id(climate_mode_text).state;
          auto current = id(climate_current_temperature).state;
          auto target = id(climate_target_temperature).state;
          // background draws ==================================================
          it.filled_rectangle(0, 0, screenwidth, screenheight, pri_color);
          it.line(0, halfscreenheight, screenwidth, halfscreenheight, off_color);
          it.line(halfscreenwidth, 0, halfscreenwidth, screenheight, off_color);
          // icons =============================================================
          // alarm icon --------------------------------------------------------
          if (id(alarm_text).state == "armed_away") {
            it.image(65, 75, icon_shield_lock, ImageAlign::CENTER, nova_color);
          } else if (id(alarm_text).state == "armed_night") {
            it.image(65, 75, icon_shield_moon, ImageAlign::CENTER, nova_color);
          } else if (id(alarm_text).state == "armed_home") {
            it.image(65, 75, icon_shield_account, ImageAlign::CENTER, blue_color);
          } else if (id(alarm_text).state == "arming") {
            it.image(65, 75, icon_shield_sync, ImageAlign::CENTER, text_color);
          } else if (id(alarm_text).state == "pending") {
            it.image(65, 75, icon_shield_lock_open, ImageAlign::CENTER, yellow_color);
          } else if (id(alarm_text).state == "triggered") {
            it.image(65, 75, icon_shield_alert, ImageAlign::CENTER, red_color);
          } else {
            it.image(65, 75, icon_shield_off, ImageAlign::CENTER, off_color);
          }
          // openings icon -----------------------------------------------------
          if (gate == "alert") {
            it.image(175, 75, icon_gate_alert, ImageAlign::CENTER, red_color);
          } else if (garage == "on" or garage_door == "on") {
            it.image(175, 75, icon_garage_open, ImageAlign::CENTER, yellow_color);
          } else if (gate == "open") {
            it.image(175, 75, icon_gate_open, ImageAlign::CENTER, blue_color);
          } else {
            it.image(175, 75, icon_gate_closed, ImageAlign::CENTER, text_color);
          }
          // thermostat icon ---------------------------------------------------
          if (mode == "auto") {
            it.image(65, 165, icon_temperature_auto, ImageAlign::CENTER, green_color);
          } else if (mode == "heat") {
            if (current < target) {
              it.image(65, 165, icon_temperature_heat, ImageAlign::CENTER, red_color);
            } else {
              it.image(65, 165, icon_temperature_cool, ImageAlign::CENTER, blue_color);
            }
          } else {
            it.image(65, 165, icon_temperature_off, ImageAlign::CENTER, text_color);
          }
          // light icon --------------------------------------------------------
          it.image(175, 165, icon_light_on, ImageAlign::CENTER, text_color);
          // foreground draws ==================================================
          it.filled_regular_polygon(halfscreenwidth, halfscreenheight -220, 144, EDGES_OCTAGON, VARIATION_FLAT_TOP, sec_color);
          it.filled_regular_polygon(halfscreenwidth, halfscreenheight +220, 144, EDGES_OCTAGON, VARIATION_FLAT_TOP, sec_color);
          it.regular_polygon(halfscreenwidth, halfscreenheight -220, 145, EDGES_OCTAGON, VARIATION_FLAT_TOP, off_color);
          it.regular_polygon(halfscreenwidth, halfscreenheight +220, 145, EDGES_OCTAGON, VARIATION_FLAT_TOP, off_color);
          // top text ==========================================================
          it.strftime(halfscreenwidth, 20, id(roboto_16_bold), orange_color, TextAlign::CENTER, "%d/%m %X", id(rtc_time).now());
          // bottom text =======================================================
          it.print(halfscreenwidth, screenheight -20, id(roboto_16_bold), orange_color, TextAlign::CENTER, "\U000F0046 QRCODE");

      # ----------------------------------------------------------- windows page
      - id: page_02
        lambda: |-
          // variables =========================================================
          float screenwidth = it.get_width();
          float screenheight = it.get_height();
          float halfscreenwidth = screenwidth /2;
          float halfscreenheight = screenheight / 2;
          // background draws ==================================================
          it.filled_rectangle(0, 0, screenwidth, screenheight, pri_color);
          // icons =============================================================
          if (id(window_01_text).state == "off") {
            it.image(60, 70, icon_bedroom_north_closed, ImageAlign::CENTER, text_color);
          } else {
            it.image(60, 70, icon_bedroom_north_open, ImageAlign::CENTER, blue_color);
          }
          if (id(window_02_text).state == "off") {
            it.image(180, 70, icon_kitchen_closed, ImageAlign::CENTER, text_color);
          } else {
            it.image(180, 70, icon_kitchen_open, ImageAlign::CENTER, blue_color);
          }
          if (id(window_03_text).state == "off") {
            it.image(halfscreenwidth, halfscreenheight, icon_velux, ImageAlign::CENTER, text_color);
          } else {
            it.image(halfscreenwidth, halfscreenheight, icon_velux, ImageAlign::CENTER, blue_color);
          }
          if (id(window_04_text).state == "off") {
            it.image(60, 170, icon_bedroom_south_closed, ImageAlign::CENTER, text_color);
          } else {
            it.image(60, 170, icon_bedroom_south_open, ImageAlign::CENTER, blue_color);
          }
          if (id(window_05_text).state == "off") {
            it.image(180, 170, icon_dining_closed, ImageAlign::CENTER, text_color);
          } else {
            it.image(180, 170, icon_dining_open, ImageAlign::CENTER, blue_color);
          }
          // foreground draws ==================================================
          it.filled_regular_polygon(halfscreenwidth, halfscreenheight -220, 144, EDGES_OCTAGON, VARIATION_FLAT_TOP, sec_color);
          it.filled_regular_polygon(halfscreenwidth, halfscreenheight +220, 144, EDGES_OCTAGON, VARIATION_FLAT_TOP, sec_color);
          it.regular_polygon(halfscreenwidth, halfscreenheight -220, 145, EDGES_OCTAGON, VARIATION_FLAT_TOP, off_color);
          it.regular_polygon(halfscreenwidth, halfscreenheight +220, 145, EDGES_OCTAGON, VARIATION_FLAT_TOP, off_color);
          // top text ==========================================================
          it.print(halfscreenwidth, 20, id(roboto_16_bold), orange_color, TextAlign::CENTER, "FENETRES");
          // bottom text =======================================================
          it.print(halfscreenwidth, screenheight -20, id(roboto_16_bold), orange_color, TextAlign::CENTER, "\U000F0046 RETOUR");

      # ------------------------------------------------------------- alarm page
      - id: page_a1
        lambda: |-
          // variables =========================================================
          float screenheight = it.get_height();
          float screenwidth = it.get_width();
          float halfscreenheight = screenheight / 2;
          float halfscreenwidth = screenwidth /2;
          auto alarm = id(alarm_text).state;
          // background draws ==================================================
          it.filled_rectangle(0, 0, screenwidth, screenheight, pri_color);
          // main icons ========================================================
          if (alarm == "armed_home") {
            it.image(60, 85, big_icon_shield_account, ImageAlign::CENTER, blue_color);
            it.image(halfscreenwidth, 85, icon_arrow_left, ImageAlign::CENTER, text_color);
          } else if (alarm == "armed_night") {
            it.image(60, 85, big_icon_shield_moon, ImageAlign::CENTER, nova_color);
            it.image(halfscreenwidth, 85, icon_arrow_left, ImageAlign::CENTER, text_color);
          } else if (alarm == "disarmed") {
            it.image(60, 85, big_icon_shield_off, ImageAlign::CENTER, off_color);
            it.image(halfscreenwidth, 85, icon_arrow_left, ImageAlign::CENTER, text_color);
          } else {
            if (alarm == "armed_away") {
              it.image(halfscreenwidth, 85, big_icon_shield_lock, ImageAlign::CENTER, nova_color);
            } else if (alarm == "arming") {
              it.image(halfscreenwidth, 85, big_icon_shield_sync, ImageAlign::CENTER, text_color);
            } else if (alarm == "pending") {
              it.image(halfscreenwidth, 85, big_icon_shield_lock_open, ImageAlign::CENTER, yellow_color);
            } else if (alarm == "triggered") {
              it.image(halfscreenwidth, 85, big_icon_shield_alert, ImageAlign::CENTER, red_color);
            }
          }
          if (alarm == "armed_home") {
            it.image(180, 85, big_icon_shield_moon, ImageAlign::CENTER, text_color);
          } else if (alarm == "armed_night" or alarm == "disarmed") {
            it.image(180, 85, big_icon_shield_account, ImageAlign::CENTER, text_color);
          }
          // main text =========================================================
          if (alarm == "armed_home") {
            it.print(halfscreenwidth, 140, id(roboto_24_regular), text_color, TextAlign::CENTER, "Armer mode nuit");
            it.print(halfscreenwidth, 180, id(roboto_20_bold), blue_color, TextAlign::CENTER, "\U000F0741 CONFIRMER");
          } else if (alarm == "armed_night" or alarm == "disarmed") {
            it.print(halfscreenwidth, 140, id(roboto_24_regular), text_color, TextAlign::CENTER, "Armer mode presence");
            it.print(halfscreenwidth, 180, id(roboto_20_bold), blue_color, TextAlign::CENTER, "\U000F0741 CONFIRMER");
          } else {
            it.print(halfscreenwidth, 140, id(roboto_24_regular), text_color, TextAlign::CENTER, "Etat actuel:");
            if (alarm == "armed_away") {
              it.print(halfscreenwidth, 180, id(roboto_20_bold), nova_color, TextAlign::CENTER, "ABSENTS");
            } else if (alarm == "arming") {
              it.print(halfscreenwidth, 180, id(roboto_20_bold), text_color, TextAlign::CENTER, "ARMEMENT");
            } else if (alarm == "pending") {
              it.print(halfscreenwidth, 180, id(roboto_20_bold), yellow_color, TextAlign::CENTER, "EN SUSPENS");
            } else if (alarm == "triggered") {
              it.print(halfscreenwidth, 180, id(roboto_20_bold), red_color, TextAlign::CENTER, "DECLENCHEE");
            }
          }
          // foreground draws ==================================================
          it.filled_regular_polygon(halfscreenwidth, halfscreenheight -220, 144, EDGES_OCTAGON, VARIATION_FLAT_TOP, sec_color);
          it.filled_regular_polygon(halfscreenwidth, halfscreenheight +220, 144, EDGES_OCTAGON, VARIATION_FLAT_TOP, sec_color);
          it.regular_polygon(halfscreenwidth, halfscreenheight -220, 145, EDGES_OCTAGON, VARIATION_FLAT_TOP, off_color);
          it.regular_polygon(halfscreenwidth, halfscreenheight +220, 145, EDGES_OCTAGON, VARIATION_FLAT_TOP, off_color);
          // top text ==========================================================
          it.print(halfscreenwidth, 20, id(roboto_16_bold), orange_color, TextAlign::CENTER, "ALARME");
          // bottom text =======================================================
          if (alarm == "armed_home" or alarm == "armed_night" or alarm == "disarmed") {
          it.print(halfscreenwidth, screenheight -20, id(roboto_16_bold), orange_color, TextAlign::CENTER, "\U000F0046 ANNULER");
          } else {
          it.print(halfscreenwidth, screenheight -20, id(roboto_16_bold), orange_color, TextAlign::CENTER, "\U000F0046 RETOUR");
          }

      # ---------------------------------------------------------- openings page
      # ____________________________________________________ gate
      - id: page_b1
        lambda: |-
          // variables =========================================================
          float screenheight = it.get_height();
          float screenwidth = it.get_width();
          float halfscreenheight = screenheight / 2;
          float halfscreenwidth = screenwidth /2;
          auto gate = id(gate_text).state;
          // background draws ==================================================
          it.filled_rectangle(0, 0, screenwidth, screenheight, pri_color);
          // main icons ========================================================
          if (gate == "closed") {
            it.image(60, 85, big_icon_gate_closed, ImageAlign::CENTER, text_color);
            it.image(halfscreenwidth, 85, icon_arrow_left, ImageAlign::CENTER, text_color);
            it.image(180, 85, big_icon_gate_open, ImageAlign::CENTER, yellow_color);
          } else if (gate == "open") {
            it.image(halfscreenwidth, 85, big_icon_gate_open, ImageAlign::CENTER, yellow_color);
          } else {
            it.image(halfscreenwidth, 85, big_icon_gate_alert, ImageAlign::CENTER, red_color);
          }
          // main text =========================================================
          if (gate == "closed") {
            it.print(halfscreenwidth, 140, id(roboto_24_regular), text_color, TextAlign::CENTER, "Ouvrir le portail?");
          } else if (gate == "open") {
            it.print(halfscreenwidth, 140, id(roboto_24_regular), text_color, TextAlign::CENTER, "Portail ouvert");
          } else {
            it.print(halfscreenwidth, 140, id(roboto_24_regular), text_color, TextAlign::CENTER, "Portail a controler!");
          }
          // + tap text --------------------------------------------------------
          if (gate == "closed") {
            it.print(halfscreenwidth, 180, id(roboto_20_bold), blue_color, TextAlign::CENTER, "\U000F0741 CONFIRMER");
          }
          // foreground draws ==================================================
          it.filled_regular_polygon(halfscreenwidth, halfscreenheight -220, 144, EDGES_OCTAGON, VARIATION_FLAT_TOP, sec_color);
          it.filled_regular_polygon(halfscreenwidth, halfscreenheight +220, 144, EDGES_OCTAGON, VARIATION_FLAT_TOP, sec_color);
          it.regular_polygon(halfscreenwidth, halfscreenheight -220, 145, EDGES_OCTAGON, VARIATION_FLAT_TOP, off_color);
          it.regular_polygon(halfscreenwidth, halfscreenheight +220, 145, EDGES_OCTAGON, VARIATION_FLAT_TOP, off_color);
          // top text ==========================================================
          it.print(halfscreenwidth, 20, id(roboto_16_bold), orange_color, TextAlign::CENTER, "\U000F1999 CHOIX");
          // bottom text =======================================================
          if (gate == "closed") {
          it.print(halfscreenwidth, screenheight -20, id(roboto_16_bold), orange_color, TextAlign::CENTER, "\U000F0046 ANNULER");
          } else {
          it.print(halfscreenwidth, screenheight -20, id(roboto_16_bold), orange_color, TextAlign::CENTER, "\U000F0046 RETOUR");
          }
      # ____________________________________________________ garage
      - id: page_b2
        lambda: |-
          // variables =========================================================
          float screenheight = it.get_height();
          float screenwidth = it.get_width();
          float halfscreenheight = screenheight / 2;
          float halfscreenwidth = screenwidth /2;
          auto garage = id(garage_text).state;
          auto garage_door = id(garage_door_text).state;
          // background draws ==================================================
          it.filled_rectangle(0, 0, screenwidth, screenheight, pri_color);
          // main icons ========================================================
          if (garage == "off") {
            if (garage_door == "on") {
              it.image(halfscreenwidth, 85, big_icon_door_open, ImageAlign::CENTER, yellow_color);
            } else {
              it.image(60, 85, big_icon_garage_closed, ImageAlign::CENTER, text_color);
              it.image(halfscreenwidth, 85, icon_arrow_left, ImageAlign::CENTER, text_color);
              it.image(180, 85, big_icon_garage_open, ImageAlign::CENTER, yellow_color);
            }
          } else {
            it.image(60, 85, big_icon_garage_open, ImageAlign::CENTER, yellow_color);
            it.image(halfscreenwidth, 85, icon_arrow_left, ImageAlign::CENTER, text_color);
            it.image(180, 85, big_icon_garage_closed, ImageAlign::CENTER, text_color);
          }
          // main text =========================================================
          if (garage == "off") {
            if (garage_door == "on") {
            it.print(halfscreenwidth, 140, id(roboto_24_regular), text_color, TextAlign::CENTER, "Garage ouvert!");
            } else {
            it.print(halfscreenwidth, 140, id(roboto_24_regular), text_color, TextAlign::CENTER, "Ouvrir le garage?");
            }
          } else {
            it.print(halfscreenwidth, 140, id(roboto_24_regular), text_color, TextAlign::CENTER, "Fermer le garage?");
          }
          // + tap text --------------------------------------------------------
          it.print(halfscreenwidth, 180, id(roboto_20_bold), blue_color, TextAlign::CENTER, "\U000F0741 CONFIRMER");
          // foreground draws ==================================================
          it.filled_regular_polygon(halfscreenwidth, halfscreenheight -220, 144, EDGES_OCTAGON, VARIATION_FLAT_TOP, sec_color);
          it.filled_regular_polygon(halfscreenwidth, halfscreenheight +220, 144, EDGES_OCTAGON, VARIATION_FLAT_TOP, sec_color);
          it.regular_polygon(halfscreenwidth, halfscreenheight -220, 145, EDGES_OCTAGON, VARIATION_FLAT_TOP, off_color);
          it.regular_polygon(halfscreenwidth, halfscreenheight +220, 145, EDGES_OCTAGON, VARIATION_FLAT_TOP, off_color);
          // top text ==========================================================
          it.print(halfscreenwidth, 20, id(roboto_16_bold), orange_color, TextAlign::CENTER, "\U000F1999 CHOIX");
          // bottom text =======================================================
          if (garage == "off") {
          it.print(halfscreenwidth, screenheight -20, id(roboto_16_bold), orange_color, TextAlign::CENTER, "\U000F0046 ANNULER");
          } else {
          it.print(halfscreenwidth, screenheight -20, id(roboto_16_bold), orange_color, TextAlign::CENTER, "\U000F0046 RETOUR");
          }

      # ------------------------------------------------------- temperature page
      - id: page_c1
        lambda: |-
          // variables =========================================================
          float screenheight = it.get_height();
          float screenwidth = it.get_width();
          float halfscreenheight = screenheight / 2;
          float halfscreenwidth = screenwidth /2;
          auto mode = id(climate_mode_text).state;
          auto current = id(climate_current_temperature).state;
          auto target = id(new_target_temperature).state;
          // background draws ==================================================
          it.filled_rectangle(0, 0, screenwidth, screenheight, pri_color);
          // foreground draws ==================================================
          it.filled_regular_polygon(halfscreenwidth, halfscreenheight -220, 144, EDGES_OCTAGON, VARIATION_FLAT_TOP, sec_color);
          it.filled_regular_polygon(halfscreenwidth, halfscreenheight +220, 144, EDGES_OCTAGON, VARIATION_FLAT_TOP, sec_color);
          it.regular_polygon(halfscreenwidth, halfscreenheight -220, 145, EDGES_OCTAGON, VARIATION_FLAT_TOP, off_color);
          it.regular_polygon(halfscreenwidth, halfscreenheight +220, 145, EDGES_OCTAGON, VARIATION_FLAT_TOP, off_color);
          // top text ==========================================================
          if (mode == "auto") {
            it.print(halfscreenwidth, 20, id(roboto_16_bold), orange_color, TextAlign::CENTER, "THERMOSTAT");
          } else {
            it.print(halfscreenwidth, 20, id(roboto_16_bold), orange_color, TextAlign::CENTER, "\U000F1999 CONSIGNE");
          }
          // main icon =========================================================
          if (mode == "auto") {
            it.image(75, 85, big_icon_temperature_auto, ImageAlign::CENTER, green_color);
          } else if (mode == "heat") {
            if (current < target) {
              it.image(75, 85, big_icon_temperature_heat, ImageAlign::CENTER, red_color);
            } else {
              it.image(75, 85, big_icon_temperature_cool, ImageAlign::CENTER, blue_color);
            }
          } else {
            it.image(75, 85, big_icon_temperature_off, ImageAlign::CENTER, off_color);
          }
          it.print(165, 85, id(roboto_20_regular), orange_color, TextAlign::CENTER, "\U000F0741 MODE");
          // main text =========================================================
          if (mode == "auto") {
            it.printf(halfscreenwidth, 140, id(roboto_24_regular), text_color, TextAlign::CENTER, "Consigne: %2.1f°C", target);
          } else if (mode == "heat") {
            it.printf(halfscreenwidth, 140, id(roboto_24_regular), text_color, TextAlign::CENTER, "Consigne: %2.1f°C", target);
          } else {
            it.printf(halfscreenwidth, 140, id(roboto_24_regular), text_color, TextAlign::CENTER, "Consigne: %2.1f°C", target);
          }
          it.printf(halfscreenwidth, 180, id(roboto_20_bold), blue_color, TextAlign::CENTER, "Sonde: %2.1f°C", current);
          // bottom text =======================================================
          it.print(halfscreenwidth, screenheight -20, id(roboto_16_bold), orange_color, TextAlign::CENTER, "\U000F0046 RETOUR");

      # ------------------------------------------------------------ lights page
      - id: page_d1
        lambda: |-
          // variables =========================================================
          float screenheight = it.get_height();
          float screenwidth = it.get_width();
          float halfscreenheight = screenheight / 2;
          float halfscreenwidth = screenwidth /2;
          auto cour = id(light_01_text).state;
          // background draws ==================================================
          it.filled_rectangle(0, 0, screenwidth, screenheight, pri_color);
          // main icons ========================================================
          if (cour == "on") {
            it.image(60, 85, big_icon_light_on, ImageAlign::CENTER, yellow_color);
            it.image(halfscreenwidth, 85, icon_arrow_left, ImageAlign::CENTER, text_color);
            it.image(180, 85, big_icon_light_off, ImageAlign::CENTER, text_color);
          } else {
            it.image(60, 85, big_icon_light_off, ImageAlign::CENTER, text_color);
            it.image(halfscreenwidth, 85, icon_arrow_left, ImageAlign::CENTER, text_color);
            it.image(180, 85, big_icon_light_on, ImageAlign::CENTER, yellow_color);
          }
          // main text =========================================================
          if (cour == "on") {
            it.print(halfscreenwidth, 140, id(roboto_24_regular), text_color, TextAlign::CENTER, "Eteindre la cour?");
          } else {
            it.print(halfscreenwidth, 140, id(roboto_24_regular), text_color, TextAlign::CENTER, "Allumer la cour?");
          }
          // + tap text --------------------------------------------------------
          it.print(halfscreenwidth, 180, id(roboto_20_bold), blue_color, TextAlign::CENTER, "\U000F0741 CONFIRMER");
          // foreground draws ==================================================
          it.filled_regular_polygon(halfscreenwidth, halfscreenheight -220, 144, EDGES_OCTAGON, VARIATION_FLAT_TOP, sec_color);
          it.filled_regular_polygon(halfscreenwidth, halfscreenheight +220, 144, EDGES_OCTAGON, VARIATION_FLAT_TOP, sec_color);
          it.regular_polygon(halfscreenwidth, halfscreenheight -220, 145, EDGES_OCTAGON, VARIATION_FLAT_TOP, off_color);
          it.regular_polygon(halfscreenwidth, halfscreenheight +220, 145, EDGES_OCTAGON, VARIATION_FLAT_TOP, off_color);
          // top text ==========================================================
          it.print(halfscreenwidth, 20, id(roboto_16_bold), orange_color, TextAlign::CENTER, "\U000F1999 CHOIX");
          // bottom text =======================================================
          it.print(halfscreenwidth, screenheight -20, id(roboto_16_bold), orange_color, TextAlign::CENTER, "\U000F0046 RETOUR");

# ======================================================================================
# ======================================================================================
# ============================================================================== sensors

sensor:

  # -------------------------------------------------------------------- climate
  - platform: homeassistant
    id: climate_target_temperature
    entity_id: $climate_id
    attribute: temperature
    on_value:
      - lambda: |-
          id(new_target_temperature).publish_state(x);
      - logger.log: "nouvelle valeur : TEST new target temperature"
  - platform: homeassistant
    id: climate_current_temperature
    entity_id: $climate_id
    attribute: current_temperature

  - platform: template
    id: new_target_temperature
    name: Target Temperature
    icon: mdi:thermometer-lines

  # ----------------------------------------------------------------- lux sensor
  - platform: homeassistant
    entity_id: $lux_sensor_id
    name: "Lux Sensor"
    id: sensor_lux
    accuracy_decimals: 0
    on_value:
      - logger.log: 
          format: "$lux_sensor_id: %.0f"
          args: [ 'id(sensor_lux).state' ]
      - if:
          condition:
            - light.is_on: backlight
          then:
            - lambda: |- 
                float lux = id(sensor_lux).state;
                if (lux < 10) { 
                  id(lcdbacklight).set_level(0.1);
                }
                else if (lux < 40) {
                  id(lcdbacklight).set_level(0.3);
                }
                else if (lux < 80) {
                  id(lcdbacklight).set_level(0.5);
                }
                else {
                  id(lcdbacklight).set_level(0.7);
                }

  # ------------------------------------------------------------- rotary encoder
  - platform: rotary_encoder
    name: Rotary Encoder
    id: rotaryencoder
    resolution: 1
    pin_a:
      number: GPIO40
      mode:
       input: true
       pullup: true
    pin_b:
      number: GPIO41
      mode:
       input: true
       pullup: true
    accuracy_decimals: 0
    # accuracy_decimals: 1
    on_clockwise:
      - logger.log: "rotary_encoder : _____________________ turned_clockwise"
      - if:
          <<: &select_off
            condition:
              - lambda: 'return id(page_selector).state == "page_off";'
            then:
              - select.set:
                  id: page_selector
                  option: page_01
              - component.update: screen
          else:
            # ____________________________________________ 0 pages
            - if:
                <<: &01_02
                  condition:
                    - lambda: 'return id(page_selector).state == "page_01";'
                  then:
                    - select.set:
                        id: page_selector
                        option: page_02
                    - component.update: screen
                else:
                  - if:
                      <<: &02_01
                        condition:
                          - lambda: 'return id(page_selector).state == "page_02";'
                        then:
                          - select.set:
                              id: page_selector
                              option: page_01
                          - component.update: screen
            # ____________________________________________ B pages
            - if:
                <<: &b1_b2
                  condition:
                    - lambda: 'return id(page_selector).state == "page_b1";'
                  then:
                    - select.set:
                        id: page_selector
                        option: page_b2
                    - component.update: screen
                else:
                  - if:
                      <<: &b2_b1
                        condition:
                          - lambda: 'return id(page_selector).state == "page_b2";'
                        then:
                          - select.set:
                              id: page_selector
                              option: page_b1
                          - component.update: screen
            # ____________________________________________ c pages
            - if:
                condition:
                  - lambda: 'return id(page_selector).state == "page_c1";'
                then:
                  - lambda: |-
                      return id(new_target_temperature).publish_state(id(new_target_temperature).state + 0.5);
              
    on_anticlockwise:
      - logger.log: "rotary_encoder : _____________________ turned_ANTIclockwise"
      - if:
          <<: *select_off
          else:
            # ____________________________________________ 0 pages
            - if:
                <<: *01_02
                else:
                  - if:
                      <<: *02_01
                      else:
            # ____________________________________________ B pages
            - if:
                <<: *b1_b2
                else:
                  - if:
                      <<: *b2_b1
                      else:
            # ____________________________________________ c pages
            - if:
                condition:
                  - lambda: 'return id(page_selector).state == "page_c1";'
                then:
                  - lambda: |-
                      return id(new_target_temperature).publish_state(id(new_target_temperature).state - 0.5);

                # id(new_target_temperature).publish_state(id(climate_target_temperature).state - 0.5);
# ======================================================================================
# ======================================================================================
# ======================================================================= binary sensors

binary_sensor:

  # --------------------------------------------------------------------- button
  - platform: gpio
    pin:
      number: GPIO42
      inverted: true
    name: M5 Button
    on_press:
      - logger.log: "button_pushed _____________________ button_pushed"
      - if:
          condition:
            - lambda: 'return id(page_selector).state == "page_01";'
          then:
            - select.set:
                id: page_selector
                option: page_off
          else:
            - select.set:
                id: page_selector
                option: page_01
            - component.update: screen

# ================================================================= touch inputs
  # -------------------------------------------------------------- welcome
  - platform: touchscreen
    id: touch_01_tl
    internal: true
    x_min: 0
    x_max: 118
    y_min: 0
    y_max: 118
    page_id: page_01
    on_release:
      - logger.log: "touch_input OVER page_01 : _____________________ top_left"
      - if:
          condition:
            - light.is_on: backlight
          then:
            - display.page.show: page_a1
            - component.update: screen
  - platform: touchscreen
    id: touch_01_tr
    internal: true
    x_min: 122
    x_max: 240
    y_min: 0
    y_max: 118
    page_id: page_01
    on_release:
      - logger.log: "touch_input OVER page_01 : _____________________ top_right"
      - if:
          condition:
            - light.is_on: backlight
          then:
            - lambda: |-
                // function ====================================================
                auto call = id(page_selector).make_call();
                // conditions ==================================================
                if (id(garage_text).state == "on") {
                call.set_option("page_b2");
                } else {
                call.set_option("page_b1");
                }
                // function call ===============================================
                call.perform();
            - component.update: screen
  - platform: touchscreen
    id: touch_01_bl
    internal: true
    x_min: 0
    x_max: 118
    y_min: 122
    y_max: 240
    page_id: page_01
    on_release:
      - logger.log: "touch_input OVER page_01 : _____________________ bottom_left"
      - if:
          condition:
            - light.is_on: backlight
          then:
            - display.page.show: page_c1
            - component.update: screen
  - platform: touchscreen
    id: touch_01_br
    internal: true
    x_min: 122
    x_max: 240
    y_min: 122
    y_max: 240
    page_id: page_01
    on_release:
      - logger.log: "touch_input OVER page_01 : _____________________ bottom_right"
      - if:
          condition:
            - light.is_on: backlight
          then:
            - display.page.show: page_d1
            - component.update: screen

  # -------------------------------------------------------------- A pages
  - platform: touchscreen
    id: touch_a1
    page_id: page_a1
    <<: &touch_full
      internal: true
      x_min: 0
      x_max: 240
      y_min: 0
      y_max: 240
    on_press:
      - if:
          condition:
            - light.is_on: backlight
          then:
            - if:
                condition:
                  or: 
                    - lambda: 'return id(alarm_text).state == "disarmed";'
                then:
                  - <<: &bip
                      rtttl.play: 'one_short:d=32,o=4,b=900:a'
                  - homeassistant.service:
                      service: input_button.press
                      data_template:
                        entity_id: input_button.alarm_arm_home
            - if:
                condition:
                  - lambda: 'return id(alarm_text).state == "armed_home";'
                then:
                  - <<: *bip
                  - homeassistant.service:
                      service: input_button.press
                      data_template:
                        entity_id: input_button.alarm_arm_night
            - if:
                condition:
                  or: 
                    - lambda: 'return id(alarm_text).state == "disarmed";'
                    - lambda: 'return id(alarm_text).state == "armed_night";'
                then:
                  - <<: *bip
                  # TEST #############################################################
                  - homeassistant.service:
                      service: input_boolean.toggle
                      data_template:
                        entity_id: input_boolean.test
                  # TEST #############################################################

  # -------------------------------------------------------------- B pages
  - platform: touchscreen
    id: touch_b1
    page_id: page_b1
    <<: *touch_full
    on_press:
      - if:
          condition:
            - light.is_on: backlight
          then:
            - <<: *bip
            - homeassistant.service:
                service: lock.unlock
                data_template:
                  entity_id: lock.gate

  - platform: touchscreen
    id: touch_b2
    page_id: page_b2
    <<: *touch_full
    on_press:
      - if:
          condition:
            - light.is_on: backlight
          then:
            - <<: *bip
            # TEST #############################################################
            - homeassistant.service:
                service: input_boolean.toggle
                data_template:
                  entity_id: input_boolean.test
            # TEST #############################################################

  # -------------------------------------------------------------- C pages
  - platform: touchscreen
    id: touch_c1
    page_id: page_c1
    <<: *touch_full
    on_press:
      - if:
          condition:
            - light.is_on: backlight
          then:
            - if:
                condition:
                  - lambda: 'return id(climate_mode_text).state == "off";'
                then:
                  - <<: *bip
                  - homeassistant.service:
                      service: climate.set_hvac_mode
                      data_template:
                        entity_id: climate.heatpump_sdgs_ebusd
                        hvac_mode: heat
            - if:
                condition:
                  or: 
                    - lambda: 'return id(climate_mode_text).state == "heat";'
                then:
                  - <<: *bip
                  - homeassistant.service:
                      service: climate.set_hvac_mode
                      data_template:
                        entity_id: climate.heatpump_sdgs_ebusd
                        hvac_mode: auto
            - if:
                condition:
                  or: 
                    - lambda: 'return id(climate_mode_text).state == "auto";'
                then:
                  - <<: *bip
                  - homeassistant.service:
                      service: climate.set_hvac_mode
                      data_template:
                        entity_id: climate.heatpump_sdgs_ebusd
                        hvac_mode: "off"

  # -------------------------------------------------------------- D pages
  - platform: touchscreen
    id: touch_d1
    page_id: page_d1
    <<: *touch_full
    on_press:
      - if:
          condition:
            - light.is_on: backlight
          then:
            - <<: *bip
            - homeassistant.service:
                service: light.toggle
                data_template:
                  entity_id: light.shelly_one_cour
images_and_icons.yaml
image:
  # ============================================================= images
  - id: img_off
    file: m5dial_files/qrcode_wifi.png
    type: GRAYSCALE
    resize: 160x160

  # ======================================================= little icons
  - id: icon_arrow_left
    file: mdi:arrow-right
    resize: 36x36

  # ============================================================== icons
  - id: icon_light_on
    file: mdi:lightbulb-group-outline
    resize: 48x48
  - id: icon_shield_lock
    file: mdi:shield-lock
    resize: 48x48
  - id: icon_shield_moon
    file: mdi:shield-moon-outline
    resize: 48x48
  - id: icon_shield_account
    file: mdi:shield-account
    resize: 48x48
  - id: icon_shield_sync
    file: mdi:shield-sync
    resize: 48x48
  - id: icon_shield_lock_open
    file: mdi:shield-lock-open
    resize: 48x48
  - id: icon_shield_alert
    file: mdi:shield-alert
    resize: 48x48
  - id: icon_shield_off
    file: mdi:shield-off
    resize: 48x48
  - id: icon_door_locked
    file: mdi:door-closed-lock
    resize: 48x48
  - id: icon_door_open
    file: mdi:door-open
    resize: 48x48
  - id: icon_garage_closed
    file: mdi:garage-variant
    resize: 48x48
  - id: icon_garage_open
    file: mdi:garage-open-variant
    resize: 48x48
  - id: icon_garage_alert
    file: mdi:garage-alert
    resize: 48x48
  - id: icon_gate_closed
    file: mdi:gate
    resize: 48x48
  - id: icon_gate_open
    file: mdi:gate-open
    resize: 48x48
  - id: icon_gate_alert
    file: mdi:gate-alert
    resize: 48x48
  - id: icon_temperature_auto
    file: mdi:thermostat-auto
    resize: 48x48
  - id: icon_temperature_heat
    file: mdi:thermometer-chevron-up
    resize: 48x48
  - id: icon_temperature_cool
    file: mdi:thermometer-chevron-down
    resize: 48x48
  - id: icon_temperature_off
    file: mdi:thermometer-off
    resize: 48x48
  - id: icon_window_closed
    file: mdi:window-closed-variant
    resize: 48x48
  - id: icon_window_open
    file: mdi:window-open-variant
    resize: 48x48
  - id: icon_velux
    file: mdi:home-roof
    resize: 48x48
  - id: icon_bedroom_north_closed
    file: mdi:bed-king
    resize: 48x48
  - id: icon_bedroom_north_open
    file: mdi:bed-king-outline
    resize: 48x48
  - id: icon_bedroom_south_closed
    file: mdi:bed-single
    resize: 48x48
  - id: icon_bedroom_south_open
    file: mdi:bed-single-outline
    resize: 48x48
  - id: icon_kitchen_closed
    file: mdi:fridge
    resize: 48x48
  - id: icon_kitchen_open
    file: mdi:fridge-top
    resize: 48x48
  - id: icon_dining_closed
    file: mdi:silverware-variant
    resize: 48x48
  - id: icon_dining_open
    file: mdi:silverware-fork-knife
    resize: 48x48

  # ========================================================== big icons
  - id: big_icon_light_off
    file: mdi:lightbulb-outline
    resize: 72x72
  - id: big_icon_light_on
    file: mdi:lightbulb-on
    resize: 72x72
  - id: big_icon_shield_lock
    file: mdi:shield-lock
    resize: 72x72
  - id: big_icon_shield_moon
    file: mdi:shield-moon-outline
    resize: 72x72
  - id: big_icon_shield_account
    file: mdi:shield-account
    resize: 72x72
  - id: big_icon_shield_sync
    file: mdi:shield-sync
    resize: 72x72
  - id: big_icon_shield_lock_open
    file: mdi:shield-lock-open
    resize: 72x72
  - id: big_icon_shield_alert
    file: mdi:shield-alert
    resize: 72x72
  - id: big_icon_shield_off
    file: mdi:shield-off
    resize: 72x72
  - id: big_icon_temperature_auto
    file: mdi:thermostat-auto
    resize: 72x72
  - id: big_icon_temperature_heat
    file: mdi:thermometer-chevron-up
    resize: 72x72
  - id: big_icon_temperature_cool
    file: mdi:thermometer-chevron-down
    resize: 72x72
  - id: big_icon_temperature_off
    file: mdi:thermometer-off
    resize: 72x72
  - id: big_icon_garage_closed
    file: mdi:garage-variant
    resize: 72x72
  - id: big_icon_garage_open
    file: mdi:garage-open-variant
    resize: 72x72
  - id: big_icon_door_open
    file: mdi:door-open
    resize: 72x72
  - id: big_icon_gate_closed
    file: mdi:gate
    resize: 72x72
  - id: big_icon_gate_open
    file: mdi:gate-open
    resize: 72x72
  - id: big_icon_gate_alert
    file: mdi:gate-alert
    resize: 72x72
colors_and_fonts.yaml
color:
  # Nova Colors
  - id: nova_color # pink
    hex: "e38de3"
  - id: pri_color # light (mantle)
    hex: "24273a"
  - id: sec_color # dark (crust)
    hex: "181926"
  - id: text_color
    hex: "cad3f5"
  - id: off_color
    hex: "5b6078"

  # Blue Colors
  - id: blue_color
    hex: "6b93e3"
  - id: green_color
    hex: "a6da95"
  - id: yellow_color
    hex: "ffd17a"
  - id: orange_color
    hex: "e0752b"
  - id: red_color
    hex: "de455c"


font:
  - id: roboto_16_bold
    file:
      type: gfonts
      family: Roboto
      weight: bold
    size: 16
    <<: &extras
      extras:
        - file: "fonts/materialdesignicons-webfont.ttf"
          glyphs: [
            "\U000F1999", # mdi-rotate-360
            "\U000F0741", # mdi-gesture-tap
            "\U000F0046", # mdi-arrow-down-thick
            ]
  - id: roboto_20_regular
    file:
      type: gfonts
      family: Roboto
      weight: regular
    size: 20
    <<: *extras
  - id: roboto_20_bold
    file:
      type: gfonts
      family: Roboto
      weight: bold
    size: 20
    <<: *extras
  - id: roboto_24_regular
    file: 
      type: gfonts
      family: Roboto
      weight: regular
    size: 24
    <<: *extras
  - id: roboto_24_bold
    file: 
      type: gfonts
      family: Roboto
      weight: bold
    size: 24
    <<: *extras
HA Automation
alias: Remote / M5 Dial / Test
description: Remote / M5Dial
trigger:
  - platform: state
    entity_id:
      - select.tab_m5stack_dial_page_selector
    to: page_01
    for: "00:00:10"
    id: auto_off
  - platform: template
    value_template: >-
      {{ not
      is_state('select.tab_m5stack_dial_page_selector',['page_off','page_01'])
      }}
    for: "00:00:20"
    id: auto_welcome
  - platform: state
    entity_id:
      - binary_sensor.pir_sonoff_hall_door_occupancy
    to: "on"
    id: auto_on
  - platform: state
    entity_id:
      - sensor.tab_m5stack_dial_target_temperature
    for: "00:00:05"
    id: target_temp
condition: []
action:
  - choose:
      - conditions:
          - condition: trigger
            id:
              - auto_off
        sequence:
          - service: select.select_option
            target:
              entity_id: select.tab_m5stack_dial_page_selector
            data:
              option: page_off
      - conditions:
          - condition: trigger
            id:
              - auto_welcome
        sequence:
          - service: select.select_option
            target:
              entity_id: select.tab_m5stack_dial_page_selector
            data:
              option: page_01
      - conditions:
          - condition: trigger
            id:
              - auto_on
          - condition: state
            entity_id: select.tab_m5stack_dial_page_selector
            state: page_off
        sequence:
          - service: select.select_option
            target:
              entity_id: select.tab_m5stack_dial_page_selector
            data:
              option: page_01
      - conditions:
          - condition: trigger
            id:
              - target_temp
        sequence:
          - service: climate.set_temperature
            target:
              entity_id: climate.heatpump_sdgs_ebusd
            data:
              temperature: "{{ states('sensor.tab_m5stack_dial_target_temperature') }}"
mode: parallel
max: 2
1 « J'aime »

il me bloque sur

INFO ESPHome 2024.5.5
INFO Reading configuration /config/esphome/m5cadran.yaml...
INFO Updating https://github.com/dgaust/esphome.git@gc9a01
INFO Detected timezone 'Europe/Paris'
INFO Detected timezone 'Europe/Paris'
WARNING GPIO3 is a strapping PIN and should only be used for I/O with care.
Attaching external pullup/down resistors to strapping pins can cause unexpected failures.
See https://esphome.io/guides/faq.html#why-am-i-getting-a-warning-about-strapping-pins
INFO Generating C++ source...
Traceback (most recent call last):
  File "/usr/local/bin/esphome", line 33, in <module>
    sys.exit(load_entry_point('esphome', 'console_scripts', 'esphome')())
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/esphome/esphome/__main__.py", line 1065, in main
    return run_esphome(sys.argv)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/esphome/esphome/__main__.py", line 1052, in run_esphome
    rc = POST_CONFIG_ACTIONS[args.command](args, config)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/esphome/esphome/__main__.py", line 428, in command_compile
    exit_code = write_cpp(config)
                ^^^^^^^^^^^^^^^^^
  File "/esphome/esphome/__main__.py", line 192, in write_cpp
    generate_cpp_contents(config)
  File "/esphome/esphome/__main__.py", line 204, in generate_cpp_contents
    CORE.flush_tasks()
  File "/esphome/esphome/core/__init__.py", line 681, in flush_tasks
    self.event_loop.flush_tasks()
  File "/esphome/esphome/coroutine.py", line 246, in flush_tasks
    next(task.iterator)
  File "/esphome/esphome/__main__.py", line 184, in wrapped
    await coro(conf)
  File "/esphome/esphome/components/image/__init__.py", line 313, in to_code
    image = Image.open(io.BytesIO(file_contents))
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/PIL/Image.py", line 3309, in open
    raise UnidentifiedImageError(msg)
PIL.UnidentifiedImageError: cannot identify image file <_io.BytesIO object at 0x7f87254860>

:sob:

j’ai mis un cs_smiley.png vide, mais ca devrait pas planter aussi vite…

Euh… en plus j’peux pas t’aider avec ça :confused:

C’est bon, en mode install, c’est good !!!
j’avais testé en mode validate.

Ah super!!

PS: si jamais vous aviez pas vu, j’étais tombé sur un concept vraiment bien!!:

Maintenant que ca cause dans l’boitier, je vais regarder tout ca de plus près !!!

1 « J'aime »

Merci, mais j’ai déjà ce qui me faut.

T’as déjà un sujet sur ce projet? c’est fini? fonctionnel?

Oui, avec OpenHASP.

2 « J'aime »

Code MAJ + ajout automatisation HA liée

Alors t’en es où?

Perso, c’était mon premier bidule ESPhome. J’ai beaucoup appris, et j’ai découvert que ESPhome intégrait LVGL maintenant… si j’ai bien compris c’est expérimental mais j’a bossé dessus et j’ai sorti une nouvelle UI! (voir lien)

C’est juste mille fois plus rapide, tant au niveau de l’affichage, que de la réponse de l’écran tactile et de la couronne. C’est vraiment top.
Je jetterai aussi un oeil à OpenHASP, par curiosité.

Il me reste la page d’alarme à créer en LVGL et je posterai ici une fois fini. Sinon, y’a le forum anglais sur lequel d’autres postent aussi leurs avancées de temps en temps.

1 « J'aime »

Allez j’ai fini mon clavier: voici mon code presque final.
image

Code
packages:
  wifi: !include common/esp_common_wifi.yaml
  device_base: !include common/esp_common_device_base.yaml

<<: !include m5dial_files/colors_and_fonts.yaml

image:
  - id: image_logo
    file: m5dial_files/cs_smiley.png
    type: RGB565
    use_transparency: true
    resize: 100x100

  - id: image_qrcode
    file: m5dial_files/qrcode_wifi.png
    type: RGB565
    resize: 160x160
      
wifi:
  manual_ip:
    static_ip: 192.168.2.63
  ap:
    ssid: "ip063-tab-m5stack-dial-fallback"

esphome:
  on_boot: 
    priority: 600
    then:
      - pcf8563.read_time: rtc_time
      - light.turn_on: backlight


esp32:
  board: esp32-s3-devkitc-1
  framework:
    type: arduino

api:
  services:
    - service: play_rtttl
      variables:
        sample: string
      then:
        - rtttl.play: !lambda 'return sample;'
  on_client_connected:
    - if:
        condition:
          lambda: 'return (0 == client_info.find("Home Assistant "));'
        then:
          - delay: 1s
          - lvgl.widget.hide: boot_screen
          - lvgl.page.show: page_welcome
          - homeassistant.service:
              service: input_select.select_option
              data:
                entity_id: $ha_page_selector
                option: page_welcome
  on_client_disconnected:
    - if:
        condition:
          lambda: 'return (0 == client_info.find("Home Assistant "));'
        then:
          - lvgl.widget.show: boot_screen

external_components:
 - source: github://clydebarrow/esphome@lvgl
   refresh: 10min
   components: [ lvgl ]

# ======================================================================================
# ======================================================================================
# =========================================================================== components

i2c:
  - id: bus_internal #required by touchscreen.ft5x06
    sda: GPIO11
    scl: GPIO12
    scan: False

touchscreen:
  platform: ft5x06
  address: 0x38
  id: my_touchscreen
  on_release:
    - if:
        condition: lvgl.is_paused
        then: &lvgl_resume
          - logger.log: "LVGL resuming"
          - lvgl.resume:
          - lvgl.widget.redraw:
          - light.turn_on: backlight
          - lvgl.page.show: page_welcome
          - homeassistant.service:
              service: input_select.select_option
              data:
                entity_id: $ha_page_selector
                option: page_welcome

uart:
  tx_pin: GPIO2
  rx_pin: GPIO1
  baud_rate: 256000
  parity: NONE
  stop_bits: 1

spi:
  mosi_pin: GPIO5
  clk_pin: GPIO6

display:
  - platform: ili9xxx
    model: gc9a01a
    auto_clear_enabled: false
    update_interval: never
    reset_pin: GPIO8
    id: my_display
    cs_pin: GPIO7
    dc_pin: GPIO4
    dimensions: 
      height: 240
      width: 240

output:
  - platform: ledc
    id: lcd_backlight
    pin: GPIO9
    min_power: 0
    max_power: 1
  - platform: ledc
    id: rtttl_out
    pin: GPIO3

light:
  - platform: monochromatic
    id: backlight
    name: "Backlight"
    output: lcd_backlight
    default_transition_length: 250ms
    on_turn_off:
      - lvgl.pause:
    on_turn_on:
      - lvgl.page.show: page_welcome
      - homeassistant.service:
          service: input_select.select_option
          data:
            entity_id: $ha_page_selector
            option: page_welcome

rtttl:
  output: rtttl_out
  id: my_rtttl

# ======================================================================================
# ======================================================================================
# ============================================================================= specials

  # -------------------------------------------------------------- substitutions
substitutions:
  name: ip063_tab_m5stack_dial
  friendly_name: M5Stack Dial
  comment: M5stack Dial / esp32-s3 (lvgl)
  log_level: ERROR # NONE / ERROR / WARN / INFO / DEBUG (default) / VERBOSE

  ha_presence_sensor: binary_sensor.ip030_mmw_aqara_living_presence_sensor_6
  ha_light_cour: light.shelly_one_cour
  ha_climate_heating: climate.heatpump_sdgs_ebusd
  ha_weather_sensor: weather.84_main_city
  ha_outside_temperature: sensor.ebusd_bass2_displayedoutsidetemp_tempv
  ha_page_selector: input_select.tab_m5stack_dial_page_selector
  ha_alarm_text: sensor.tplt_alarmo
  ha_alarm_state: alarm_control_panel.alarme_maison
  ha_front_door: binary_sensor.magnet_aqara_front_door_contact
  ha_window_group: binary_sensor.windows_upstairs_bedrooms
  ha_handgate_text: binary_sensor.tplt_gate_open_too_long
  ha_cargate_text: input_boolean.test
  ha_garage_state: binary_sensor.io_frient_garage_09c6_input_1

  # -------------------------------------------------------------------- globals
globals:
  - id: lambda_nova_color
    type: lv_color_t
    restore_value: no
    initial_value: 'lv_color_hex(0xe38de3)'

  - id: lambda_blue_color
    type: lv_color_t
    restore_value: no
    initial_value: 'lv_color_hex(0x6b93e3)'

  - id: lambda_green_color
    type: lv_color_t
    restore_value: no
    initial_value: 'lv_color_hex(0xa6da95)'

  - id: lambda_yellow_color
    type: lv_color_t
    restore_value: no
    initial_value: 'lv_color_hex(0xffd17a)'

  - id: lambda_orange_color
    type: lv_color_t
    restore_value: no
    initial_value: 'lv_color_hex(0xe0752b)'

  - id: lambda_red_color
    type: lv_color_t
    restore_value: no
    initial_value: 'lv_color_hex(0xede455c)'

  - id: lambda_off_color
    type: lv_color_t
    restore_value: no
    initial_value: 'lv_color_hex(0x85b6078)'

  - id: lambda_text_color
    type: lv_color_t
    restore_value: no
    initial_value: 'lv_color_hex(0xcad3f5)'

# ======================================================================================
# ======================================================================================
# =============================================================================== number
number:

  # __________________________________________________ backlight timeout
  - platform: template
    name: Backlight Timeout
    optimistic: true
    id: backlight_timeout
    unit_of_measurement: "s"
    initial_value: 30
    restore_value: true
    min_value: 10
    max_value: 90
    step: 10
    mode: auto # auto / box / slider

# ======================================================================================
# ======================================================================================
# ================================================================================= time

switch:
  - platform: template
    id: switch_antiburn
    icon: mdi:television-shimmer
    optimistic: true
    entity_category: "config"
    turn_on_action:
      - logger.log: "Starting Antiburn"
      - if:
          condition: lvgl.is_paused
          then:
            - lvgl.resume:
            - lvgl.widget.redraw:
            - delay: 1s
      - lvgl.pause:
          show_snow: true
    turn_off_action:
      - logger.log: "Stopping Antiburn"
      - if:
          condition: lvgl.is_paused
          then:
            - lvgl.resume:
            - lvgl.widget.redraw:
            - delay: 1s
            - lvgl.pause:

# ======================================================================================
# ======================================================================================
# =============================================================================== switch

time:
  - platform: homeassistant
    on_time_sync:
      then:
        - pcf8563.write_time: rtc_time
        - logger.log: "Time Synced"
  - platform: pcf8563
    id: rtc_time
    address: 0x38
    update_interval: never
    on_time:
      - minutes: '*'
        seconds: 0
        then:
          - script.execute: time_update
      - hours: 2,3,4,5
        minutes: 5
        seconds: 0
        then:
          - switch.turn_on: switch_antiburn
      - hours: 2,3,4,5
        minutes: 35
        seconds: 0
        then:
          - switch.turn_off: switch_antiburn
      - minutes: '*'
        seconds: 0,10,20,30,40,50
        then:
          - lvgl.widget.hide: label_outside_temperature
          - lvgl.widget.show: icon_weather_sensor
      - minutes: '*'
        seconds: 5,15,25,35,45,55
        then:
          - lvgl.widget.show: label_outside_temperature
          - lvgl.widget.hide: icon_weather_sensor

# ======================================================================================
# ======================================================================================
# =============================================================================== script

script:
  - id: time_update
    then:
      - lvgl.label.update:
          id: label_rtc_time
          text: !lambda |-
            static char time_buf[10];
            auto now = id(rtc_time).now();
            int hour = now.hour;
            int minute = now.minute;
            snprintf(time_buf, sizeof(time_buf), "%02d:%02d", hour, minute);
            return time_buf;
      - lvgl.label.update:
          id: label_rtc_date
          text: !lambda |-
            static const char * const month_names[] = {"Janvier", "Fevrier", "Mars", "Avril", "Mai", "Juin", "Juillet", "Aout", "Sept.", "Oct.", "Nov.", "Dec."};
            static const char * const day_names[] = {"Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"};
            static char combined_buf[20];
            auto now = id(rtc_time).now();
            snprintf(combined_buf, sizeof(combined_buf), "%s %2d %s", day_names[now.day_of_week - 1] , now.day_of_month , month_names[now.month-1]);
            return combined_buf;
            
  - id: climate_set_target_temperature
    mode: restart
    then:
      - delay: 5s
      - homeassistant.service:
          service: climate.set_temperature
          data:
            entity_id: $ha_climate_heating
            temperature: !lambda "return id(climate_heating_new_target).state;"
            
  - id: alarm_container_blink
    mode: restart
    then:
      # - delay: 500ms
      - if:
          condition:
            - lambda: 'return id(alarm_state).state == "triggered";'
          then:
            - repeat:
                count: 15
                then:
                  - lvgl.widget.hide: container_alarm_state
                  - delay: 250ms
                  - lvgl.widget.show: container_alarm_state
                  - delay: 750ms
          # else:
      - if:
          condition:
            - lambda: 'return id(alarm_state).state == "pending";'
          then:
            - repeat:
                count: 10
                then:
                  - lvgl.widget.hide: container_alarm_state
                  - delay: 500ms
                  - lvgl.widget.show: container_alarm_state
                  - delay: 1s
                # else:
      - if:
          condition:
            - lambda: 'return id(alarm_state).state == "arming";'
          then:
            - repeat:
                count: 10
                then:
                  - lvgl.widget.hide: container_alarm_state
                  - delay: 1s
                  - lvgl.widget.show: container_alarm_state
                  - delay: 3s

# ======================================================================================
# ======================================================================================
# ========================================================================== text sensor
text_sensor:

  # ------------------------------------------------------------- home assistant

  # ______________________________________________________ page selector
  - platform: homeassistant
    id: page_selector_state
    entity_id: $ha_page_selector
    on_value:
      - if:
          condition: lvgl.is_paused
          then: *lvgl_resume
      - if:
          condition:
            - lambda: 'return id(page_selector_state).state == "page_welcome";'
          then:
            - lvgl.page.show: page_welcome
      - if:
          condition:
            - lambda: 'return id(page_selector_state).state == "page_thermostat";'
          then:
            - lvgl.page.show: page_thermostat
      - if:
          condition:
            - lambda: 'return id(page_selector_state).state == "page_alarm";'
          then:
            - lvgl.page.show: page_alarm
      - if:
          condition:
            - lambda: 'return id(page_selector_state).state == "page_qrcode";'
          then:
            - lvgl.page.show: page_qrcode
      # - if:
      #     condition:
      #       - lambda: 'return id(page_selector_state).state == "page_test";'
      #     then:
      #       - lvgl.page.show: page_test

  # ________________________________________________________ alarm state
  - platform: homeassistant
    id: alarm_state
    entity_id: $ha_alarm_state
    on_value:
      - if:
          condition: lvgl.is_paused
          then: *lvgl_resume
      - lvgl.page.show: page_welcome
      - homeassistant.service:
          service: input_select.select_option
          data:
            entity_id: $ha_page_selector
            option: page_welcome
      - if:
          condition:
            - lambda: 'return id(alarm_state).state == "arming" or id(alarm_state).state == "pending" or id(alarm_state).state == "triggered";'
          then:
            - script.execute: alarm_container_blink
          else:
            - script.stop: alarm_container_blink
            - lvgl.widget.show: container_alarm_state
      - lvgl.widget.update:
          id: container_alarm_state
          bg_color: !lambda |-
            if (x == "armed_away") {
              return id(lambda_nova_color);
            } else if (x == "armed_home") {
              return id(lambda_blue_color);
            } else if (x == "armed_night") {
              return id(lambda_nova_color);
            } else if (x == "arming" or x == "pending") {
              return id(lambda_yellow_color);
            } else if (x == "triggered") {
              return id(lambda_red_color);
            } else if (x == "disarmed") {
              return id(lambda_text_color);
            } else {
              return id(lambda_off_color);
            }

  # _________________________________________________________ alarm text
  - platform: homeassistant
    id: alarm_text
    entity_id: $ha_alarm_text
    on_value:
      - lvgl.label.update:
          id: label_alarm_text
          text:
            format: "%s"
            args: [ 'x.c_str()' ]
    
  # ___________________________________________________ front door state
  - platform: homeassistant
    id: front_door # off / on
    entity_id: $ha_front_door
    on_value:
      - if:
          condition: lvgl.is_paused
          then: *lvgl_resume
      - lvgl.widget.update:
          id: icon_front_door
          text_color: !lambda |-
            if (x == "off") {
              return id(lambda_text_color);
            } else if (x == "on") {
              return id(lambda_blue_color);
            } else {
              return id(lambda_off_color);
            }
      - lvgl.label.update:
          id: icon_front_door
          text: !lambda |-
            if (x == "off") {
              return "\U000F10AF"; // mdi:door-closed-lock
            } else if (x == "on") {
              return "\U000F081C"; // mdi:door-open
            } else {
              return "x";
            }
    
  # _________________________________________________ window group state
  - platform: homeassistant
    id: window_group # off / on
    entity_id: $ha_window_group
    on_value:
      - if:
          condition: lvgl.is_paused
          then: *lvgl_resume
      - lvgl.widget.update:
          id: icon_window_group
          text_color: !lambda |-
            if (x == "off") {
              return id(lambda_text_color);
            } else if (x == "on") {
              return id(lambda_yellow_color);
            } else {
              return id(lambda_off_color);
            }
      - lvgl.label.update:
          id: icon_window_group
          text: !lambda |-
            if (x == "off") {
              return "\U000F11DB"; // mdi:window-closed-variant
            } else if (x == "on") {
              return "\U000F11DC"; // mdi:window-open-variant
            } else {
              return "x";
            }

  # _______________________________________________________ garage state
  - platform: homeassistant
    id: garage_state # off / on
    entity_id: $ha_garage_state
    on_value:
      - if:
          condition: lvgl.is_paused
          then: *lvgl_resume
      - lvgl.widget.update:
          id: icon_garage_state
          text_color: !lambda |-
            if (x == "off") {
              return id(lambda_text_color);
            } else if (x == "on") {
              return id(lambda_yellow_color);
            } else {
              return id(lambda_off_color);
            }
      - lvgl.label.update:
          id: icon_garage_state
          text: !lambda |-
            if (x == "off") {
              return "\U000F12D3"; // mdi:garage-variant
            } else if (x == "on") {
              return "\U000F12D4"; // mdi:garage-open-variant
            } else {
              return "x";
            }

  # ______________________________________________________ cargate state
  - platform: homeassistant
    id: cargate_text # off / on
    entity_id: $ha_cargate_text
    on_value:
      - if:
          condition: lvgl.is_paused
          then: *lvgl_resume
      - lvgl.widget.update:
          id: icon_cargate_text
          text_color: !lambda |-
            if (x == "off") {
              return id(lambda_text_color);
            } else if (x == "on") {
              return id(lambda_yellow_color);
            } else {
              return id(lambda_off_color);
            }
      - lvgl.label.update:
          id: icon_cargate_text
          text: !lambda |-
            if (x == "off") {
              return "\U000F0E8B"; // mdi:boom-gate-outline
            } else if (x == "on") {
              return "\U000F0E87"; // mdi:boom-gate-alert
            } else {
              return "x";
            }

  # ______________________________________________________ handgate text
  - platform: homeassistant
    id: handgate_text # closed / open / alert
    entity_id: $ha_handgate_text
    attribute: esphome_text_sensor
    filters:
      - map:
        - closed -> Ferme
        - open -> Ouvert
        - alert -> Defaut
    on_raw_value:
      - if:
          condition: lvgl.is_paused
          then: *lvgl_resume
      - lvgl.label.update:
          id: icon_handgate_text
          text: !lambda |-
            if (x == "closed") {
              return "\U000F0299"; // mdi:gate
            } else if (x == "open") {
              return "\U000F116A"; // mdi:gate-open
            } else if (x == "alert") {
              return "\U000F17F8"; // mdi:gate-alert
            } else {
              return "x";
            }
      - lvgl.widget.update:
          id: icon_handgate_text
          text_color: !lambda |-
            if (x == "closed") {
              return id(lambda_text_color);
            } else if (x == "open") {
              return id(lambda_yellow_color);
            } else if (x == "alert") {
              return id(lambda_red_color);
            } else {
              return id(lambda_off_color);
            }

  # ______________________________________________________ climate state
  - platform: homeassistant
    id: climate_heating_state
    entity_id: $ha_climate_heating
    on_value:
      - lvgl.label.update:
          id: icon_climate_heating_0
          text: !lambda |-
            if (x == "auto") {
              return "\U000F1B17"; // mdi:thermostat-auto
            } else if (x == "heat") {
              return "\U000F0238"; // mdi:fire
            } else if (x == "off") {
              return "\U000F0AD8"; // mdi:radiator-off
            } else {
              return "x";
            }
      - lvgl.label.update:
          id: icon_climate_heating_1
          text: !lambda |-
            if (x == "auto") {
              return "\U000F1B17"; // mdi:thermostat-auto
            } else if (x == "heat") {
              return "\U000F0238"; // mdi:fire
            } else if (x == "off") {
              return "\U000F0AD8"; // mdi:radiator-off
            } else {
              return "x";
            }
      - lvgl.widget.update:
          id: icon_climate_heating_1
          text_color: !lambda |-
            if (x == "auto") {
              return id(lambda_green_color);
            } else if (x == "heat") {
              return id(lambda_orange_color);
            } else if (x == "off") {
              return id(lambda_text_color);
            } else {
              return id(lambda_off_color);
            }
      - lvgl.widget.update:
          id: icon_climate_heating_0
          text_color: !lambda |-
            if (x == "auto") {
              return id(lambda_green_color);
            } else if (x == "heat") {
              return id(lambda_orange_color);
            } else if (x == "off") {
              return id(lambda_text_color);
            } else {
              return id(lambda_off_color);
            }
      - if:
          condition:
            - lambda: 'return id(climate_heating_state).state == "auto";'
          then:
            - lvgl.widget.hide: meter_climate_heating_target
            - lvgl.widget.hide: arc_climate_heating_target
          else:
            - lvgl.widget.show: meter_climate_heating_target
            - lvgl.widget.show: arc_climate_heating_target

  # _____________________________________________________ weather sensor
  - platform: homeassistant
    id: weather_sensor
    entity_id: $ha_weather_sensor
    on_value:
      - lvgl.label.update:
          id: icon_weather_sensor
          text: !lambda |-
            if (x == "clear-night") {
              return "\U000F0594";
            } else if (x == "cloudy") {
              return "\U000F0590";
            } else if (x == "exceptional") {
              return "\U000F0F2F";
            } else if (x == "fog") {
              return "\U000F0591";
            } else if (x == "hail") {
              return "\U000F0592";
            } else if (x == "lightning") {
              return "\U000F0593";
            } else if (x == "lightning-rainy") {
              return "\U000F067E";
            } else if (x == "partlycloudy") {
              return "\U000F0595";
            } else if (x == "pouring") {
              return "\U000F0596";
            } else if (x == "rainy") {
              return "\U000F0597";
            } else if (x == "snowy") {
              return "\U000F0598";
            } else if (x == "snowy-rainy") {
              return "\U000F067F";
            } else if (x == "sunny") {
              return "\U000F0599";
            } else if (x == "windy") {
              return "\U000F059D";
            } else if (x == "windy-variant") {
              return "\U000F059E";
            } else if (x == "sunny-off") {
              return "\U000F14E4";
            } else {
              return "x";
            }

  # _________________________________________________________ light cour
  - platform: homeassistant
    id: light_cour
    entity_id: $ha_light_cour
    on_value:
      - if:
          condition: lvgl.is_paused
          then: *lvgl_resume
      - lvgl.label.update:
          id: icon_light_cour
          text: !lambda |-
            if (x == "on") {
              return "\U000F06E8"; // mdi:lightbulb-on
            } else if (x == "off") {
              return "\U000F0336"; // mdi:lightbulb-outline
            } else {
              return "x";
            }
      - lvgl.widget.update:
          id: icon_light_cour
          text_color: !lambda |-
            if (x == "on") {
              return id(lambda_yellow_color);
            } else if (x == "off") {
              return id(lambda_text_color);
            } else {
              return id(lambda_off_color);
            }

# ======================================================================================
# ======================================================================================
# ======================================================================== binary sensor

binary_sensor:

  # --------------------------------------------------------------------- button
  - platform: gpio
    pin:
      number: GPIO42
      inverted: true
    id: m5_button
    name: M5 Button
    on_press:
      - if:
          condition: lvgl.is_paused
          then: *lvgl_resume
      - lvgl.page.show: page_welcome
      - homeassistant.service:
          service: input_select.select_option
          data:
            entity_id: $ha_page_selector
            option: page_welcome

  # ------------------------------------------------------------- home assistant

  # ____________________________________________________ presence sensor
  - platform: homeassistant
    id: presence_sensor
    entity_id: $ha_presence_sensor
    publish_initial_state: true
    on_state:
      - if:
          condition:
            - binary_sensor.is_on: presence_sensor
          then:
            - if:
                condition: lvgl.is_paused
                then: *lvgl_resume
            - lvgl.page.show: page_welcome
            - homeassistant.service:
                service: input_select.select_option
                data:
                  entity_id: $ha_page_selector
                  option: page_welcome

# ======================================================================================
# ======================================================================================
# =============================================================================== sensor
sensor:

  # ------------------------------------------------------------- rotary encoder
  - platform: rotary_encoder
    id: rotaryencoder
    resolution: 1
    pin_a: 
      number: GPIO40
      mode:
       input: true
       pullup: true
    pin_b: 
      number: GPIO41
      mode:
       input: true
       pullup: true
    accuracy_decimals: 0
    # ________________________________________________________ clockwise
    on_clockwise:
      - if:
          condition: lvgl.is_paused
          then: *lvgl_resume
          else:
            - if:
                condition:
                  - lambda: 'return id(page_selector_state).state == "page_thermostat";'
                then:
                  - lambda: |-
                      return id(climate_heating_new_target).publish_state(id(climate_heating_new_target).state + 0.5);
                else:
                  - lvgl.page.next:
                      animation: FADE_OUT
                      time: 250ms
    # ___________________________________________________ anti-clockwise
    on_anticlockwise:
      - if:
          condition: lvgl.is_paused
          then: *lvgl_resume
          else:
            - if:
                condition:
                  - lambda: 'return id(page_selector_state).state == "page_thermostat";'
                then:
                  - lambda: |-
                      return id(climate_heating_new_target).publish_state(id(climate_heating_new_target).state - 0.5);
                else:
                  - lvgl.page.previous:
                      animation: FADE_IN
                      time: 250ms

  # ------------------------------------------------------------- home assistant

  # _____________________________________________ climate heating target
  - platform: homeassistant
    id: climate_heating_target
    entity_id: $ha_climate_heating
    attribute: temperature
    on_value:
      - if:
          condition: lvgl.is_paused
          then: *lvgl_resume
      - lambda: |-
          id(climate_heating_new_target).publish_state(x);

  # ____________________________________________ climate new targetvalue
  - platform: template
    id: climate_heating_new_target
    icon: mdi:thermometer-lines
    filters:
      - clamp:
          min_value: 16 # idem meter range_from value
          max_value: 24 # idem meter range_from value
    on_value:
      - lvgl.arc.update:
          id: arc_climate_heating_target
          value: !lambda return x * 10;
      - script.execute: climate_set_target_temperature

  # ____________________________________________ climate heating current
  - platform: homeassistant
    id: climate_heating_current
    entity_id: $ha_climate_heating
    attribute: current_temperature
    on_value:
      - lvgl.label.update:
          id: label_climate_heating_current_0
          text:
            format: "%2.1f°C"
            args: [ 'x' ]
      - lvgl.label.update:
          id: label_climate_heating_current_1
          text:
            format: "%2.1f°C"
            args: [ 'x' ]

  # ________________________________________________ outside temperature
  - platform: homeassistant
    id: outside_temperature
    entity_id: $ha_outside_temperature
    on_value:
      - lvgl.label.update:
          id: label_outside_temperature
          text:
            format: "%2.1f°C"
            args: [ 'x' ]

# ======================================================================================
# ======================================================================================
# ======================================================================== key collector
key_collector:
  - source_id: keypad_alarm
    min_length: 4
    max_length: 4
    end_keys: "#"
    end_key_required: true
    # back_keys: "*"
    clear_keys: "*"
    allowed_keys: "0123456789*#"
    timeout: 3s
    on_progress:
      - if:
          condition:
            - lambda: return (0 != x.compare(std::string{""}));
          then:
            - lvgl.label.update:
                id: label_alarm_code
                text: !lambda |-
                  if (x.length() == 1) {
                    return "*";
                  } else if (x.length() == 2) {
                    return "**";
                  } else if (x.length() == 3) {
                    return "***";
                  } else if (x.length() == 4) {
                    return "****";
                  } else {
                    return "";
                  }
            # - lvgl.label.update:
            #     id: label_alarm_code
            #     text: !lambda 'return x.c_str();'
            - lvgl.widget.update:
                id: alarm_code
                bg_color: text_color
      - if:
          condition:
            - lambda: return (0 == x.compare(std::string{""}));
          then:
            - lvgl.label.update:
                id: label_alarm_code
                text: "Code + \uF00C"
            - lvgl.widget.update:
                id: alarm_code
                bg_color: text_color
    on_timeout:
      - lvgl.label.update:
          id: label_alarm_code
          text: "Code + \uF00C"
      - lvgl.widget.update:
          id: alarm_code
          bg_color: text_color
    on_result:
      - homeassistant.service:
          service: alarm_control_panel.alarm_arm_home
          data:
            entity_id: $ha_alarm_state
            code: !lambda 'return x;'
      - delay: 1s
      - if:
          condition:
            - lambda: 'return id(alarm_state).state == "armed_home";'
          then:
            - <<: &one_short
                rtttl.play: 'one_short:d=24,o=5,b=100:g'
            - lvgl.label.update:
                id: label_alarm_code
                text: "OK"
            - lvgl.widget.update:
                id: alarm_code
                bg_color: green_color
            - delay: 1s
            - lvgl.page.show: page_welcome
            - homeassistant.service:
                service: input_select.select_option
                data:
                  entity_id: $ha_page_selector
                  option: page_welcome
          else:
            - <<: &three_short
                rtttl.play: 'three_short:d=4,o=5,b=100:16e6,16e6,16e6'
            - lvgl.label.update:
                id: label_alarm_code
                text: "Code Faux"
            - lvgl.widget.update:
                id: alarm_code
                bg_color: red_color

# ======================================================================================
# ======================================================================================
# ================================================================================= LVGL
lvgl:

  # -------------------------------------------------------------------- general
  displays:
    - my_display
  touchscreens:
    - my_touchscreen
  rotary_encoders:
    sensor: rotaryencoder
    enter_button: m5_button
  theme:
    obj:
      scrollbar_mode: "OFF"
      scrollable: false
      bg_color: pri_color
      border_width: 0
      border_color: orange_color
      radius: 10
    arc:
      arc_color: pri_color
      knob:
        bg_color: nova_color
      indicator:
        arc_color: dark_nova_color
    btn:
      bg_color: pri_color
      checked:
        bg_color: nova_color
    label:
      align: CENTER
      text_font: montserrat_18
      text_color: text_color
      clickable: false
    led:
      align: CENTER
    img:
      align: CENTER
  on_idle:
    timeout: !lambda "return (id(backlight_timeout).state * 1000);"
    then:
      - logger.log: "LVGL is idle"
      - light.turn_off: backlight
      - lvgl.pause:

  # ------------------------------------------------------------------ top layer
  top_layer:
    widgets:
      # çççççççççççççççççççççççççççççççççççççççççççççç CONTAINER
      - obj:
          id: boot_screen
          width: SIZE_CONTENT 
          height: SIZE_CONTENT 
          bg_color: 0x000000 # black
          bg_opa: COVER
          radius: 0
          pad_all: 0
          border_width: 0
          on_press:
            - lvgl.widget.hide: boot_screen
          widgets:

            # ''''''''''''''''''''''''''''''''''''''''''''' logo
            - img:
                align: CENTER
                src: image_logo
                y: -30

            # '''''''''''''''''''''''''''''''''''''''''' loading
            - spinner:
                align: CENTER
                y: 70
                height: 50
                width: 50
                spin_time: 2s
                arc_length: 60deg
                arc_rounded: true
                arc_color: sec_color
                arc_width: 7
                indicator:
                  arc_color: blue_color
                  arc_width: 7

  # ---------------------------------------------------------------------- pages
  pages:
  
    # _____________________________________________________ page welcome
    - id: page_welcome
      <<: &page_config
        bg_color: sec_color
        scrollbar_mode: "OFF"
        scrollable: false
      widgets:
        # ''''''''''''''''''''''''''''''''''''''''''''''''' time
        - label:
            id: label_rtc_time
            align: TOP_MID
            text: "Heure"
            y: 5

        # ''''''''''''''''''''''''''''''''''''''''''''''''' date
        - label:
            id: label_rtc_date
            align: TOP_MID
            text_font: montserrat_16
            text: "Date"
            y: 29

        # ççççççççççççççççççççççççççççççççççççç MIDDLE_CONTAINER
        - obj:
            id: container_alarm_state
            align: BOTTOM_MID
            x: 0
            y: -150
            width: 250
            height: 36
            clickable: true
            on_long_press:
              - if:
                  condition:
                    - lambda: 'return id(alarm_state).state == "armed_night";'
                  then:
                    - lvgl.label.update:
                        id: label_alarm_code
                        text: "Code + \uF00C"
                    - lvgl.widget.update:
                        id: alarm_code
                        bg_color: text_color
                    - lvgl.page.show: page_alarm
                    - homeassistant.service:
                        service: input_select.select_option
                        data:
                          entity_id: $ha_page_selector
                          option: page_alarm
                  else:
                    - if:
                        condition:
                          - lambda: 'return id(alarm_state).state == "armed_home";'
                        then:
                          - homeassistant.service:
                              service: alarm_control_panel.alarm_arm_night
                              data:
                                entity_id: $ha_alarm_state
                                code: !secret arming_code
                          - delay: 1s
                          - if:
                              condition:
                                - lambda: 'return id(alarm_state).state == "armed_night";'
                              then:
                                - <<: *one_short
                              else:
                                - <<: *three_short
                                - repeat:
                                    count: 5
                                    then:
                                      - lvgl.widget.hide: container_openings
                                      - delay: 200ms
                                      - lvgl.widget.show: container_openings
                                      - delay: 200ms
                        else:
                          - <<: *three_short
                          - lvgl.widget.hide: container_alarm_state
                          - delay: 200ms
                          - lvgl.widget.show: container_alarm_state
                          - delay: 200ms
            widgets:

              # '''''''''''''''''''''''''''''''''''''''''' alarm
              - label:
                  id: label_alarm_text
                  text: "Alarme"
                  text_color: sec_color

        # ççççççççççççççççççççççççççççççççççççç LEFT_CONTAINER_1
        - obj:
            width: 150
            height: 95
            align: TOP_RIGHT
            x: -101
            y: 98
            widgets:

              ######################################## COLUMN 01

              # ''''''''''''''''''''''''''''''''''''''''' garage
              - label:
                  id: icon_garage_state
                  <<: &icon_40_config
                    clickable: true
                    text_font: grandstander_40_regular
                    text: "x"
                  <<: &column_01
                    x: -60
                  <<: &row_01
                    align: TOP_RIGHT
                    y: -8
                  clickable: false
                  # on_long_press:
                  #   - <<: *one_short

              # '''''''''''''''''''''''''''''''''''''''' cargate
              - label:
                  id: icon_cargate_text
                  <<: *icon_40_config
                  <<: *column_01
                  <<: &row_02
                    align: BOTTOM_RIGHT
                    y: 2
                  clickable: false
                  # on_long_press:
                  #   - <<: *one_short

              ######################################## COLUMN 02
 
              # '''''''''''''''''''''''''''''''''''''''''' light
              - label:
                  id: icon_light_cour
                  <<: *icon_40_config
                  <<: &column_02
                    x: 0
                  <<: *row_01
                  on_long_press:
                    - <<: *one_short
                    - homeassistant.service:
                        service: light.toggle
                        data:
                          entity_id: $ha_light_cour
                    - repeat:
                        count: 3
                        then:
                          - lvgl.widget.hide: icon_light_cour
                          - delay: 200ms
                          - lvgl.widget.show: icon_light_cour
                          - delay: 200ms

              # ''''''''''''''''''''''''''''''''''''''' handgate
              - label:
                  id: icon_handgate_text
                  <<: *icon_40_config
                  <<: *column_02
                  <<: *row_02
                  y: 4 # SPE handgate (mdi:icon not centered)
                  on_long_press:
                    - <<: *one_short
                    - homeassistant.service:
                        service: lock.unlock
                        data_template:
                          entity_id: lock.gate
                    - repeat:
                        count: 3
                        then:
                          - lvgl.widget.hide: icon_handgate_text
                          - delay: 200ms
                          - lvgl.widget.show: icon_handgate_text
                          - delay: 200ms

        # ççççççççççççççççççççççççççççççççççççç LEFT_CONTAINER_2                          
        - obj:
            bg_opa: TRANSP
            border_opa: TRANSP
            width: 150
            height: 45
            align: TOP_RIGHT
            x: -100
            y: 195
            widgets:

              # '''''''''''''''''''''''''''' outside temperature
              - label:
                  id: label_outside_temperature
                  x: 41
                  text_font: montserrat_20
                  text: "Temp"

              # '''''''''''''''''''''''''''''''''''''''''' meteo
              - label:
                  id: icon_weather_sensor
                  x: 41
                  text_font: grandstander_36_regular
                  text: "x"

        # çççççççççççççççççççççççççççççççççççç RIGHT_1_CONTAINER
        - obj:
            id: container_openings
            bg_opa: TRANSP
            border_opa: TRANSP
            width: 110
            height: 45
            align: TOP_LEFT
            x: 147
            y: 98
            widgets:

              # ''''''''''''''''''''''''''''''''''''' front door
              - label:
                  id: icon_front_door
                  <<: &icon_36_config
                    text_font: grandstander_36_regular
                    text: "x"
                  x: -5
                  <<: &row
                    align: LEFT_MID
                    y: 0

              # '''''''''''''''''''''''''''''''''''''''' windows
              - label:
                  id: icon_window_group
                  <<: *icon_36_config
                  x: 40 # +45
                  <<: *row

        # çççççççççççççççççççççççççççççççççççç RIGHT_2_CONTAINER
        - obj:
            width: 110
            height: 100
            align: TOP_LEFT
            x: 147
            y: 151
            on_press:
              - lvgl.page.show: page_thermostat
              - homeassistant.service:
                  service: input_select.select_option
                  data:
                    entity_id: $ha_page_selector
                    option: page_thermostat
            widgets:

              # '''''''''''''''''''''''''''' current temperature
              - label:
                  id: label_climate_heating_current_0
                  y: -5
                  align: TOP_LEFT
                  text_font: montserrat_20
                  text: "Temp"

              # '''''''''''''''''''''''''''''''''' climate state
              - label:
                  id: icon_climate_heating_0
                  align: TOP_LEFT
                  x: -3
                  y: 22
                  <<: &icon_32_config
                    text_font: grandstander_32_regular
                    text: "x"

    # ______________________________________________________ page qrcode
    - id: page_qrcode
      bg_color: 0xFFFFFF # white
      widgets:
        - img:
            src: image_qrcode

    # # ________________________________________________________ page test
    # - id: page_test
    #   <<: *page_config
    #   widgets:
    #     - label:
    #         align: center
    #         text: "TEST"
    #         y: -50
    #     # # A TESTER
    #     # - label:
    #     #   align_to:
    #     #     id: obj_id
    #     #     align: OUT_RIGHT_MID
    #     #     x: 17

    # __________________________________________________ page thermostat
    - id: page_thermostat
      skip: true
      <<: *page_config
      widgets:

        # '''''''''''''''''''''''''''''''''''''''''''''''''' arc
        - arc:
            id: arc_climate_heating_target
            width: 210
            height: 210
            align: CENTER
            start_angle: 145 # = meter rotation value
            end_angle: 35
            min_value: 160 # idem meter range_from value * 10
            max_value: 240 # idem meter range_to value * 10
            adjustable: false

        # '''''''''''''''''''''''''''''''''''''''''' arc numbers
        - meter:
            id: meter_climate_heating_target
            width: 215
            height: 215
            align: CENTER
            bg_opa: TRANSP
            border_width: 0
            text_color: text_color
            scales:
              range_from: 16 # = template_sensor, and arc values
              range_to: 24 # = template_sensor, and arc values
              angle_range: 250
              rotation: 145 # = arc start_angle
              ticks:
                count: 9
                width: 1
                length: 3
                color: sec_color
                major:
                  stride: 1
                  length: 1
                  color: dark_nova_color
                  label_gap: 18

        # ''''''''''''''''''''''''''''''''''''''' climate button
        - obj:
            id: button_climate_toggle
            align: CENTER
            width: 60
            height: 60
            radius: 60
            on_short_click:
              - if:
                  condition:
                    - lambda: 'return id(climate_heating_state).state == "auto";'
                  then:
                    - <<: *one_short
                    - homeassistant.service:
                        service: climate.set_hvac_mode
                        data:
                          entity_id: $ha_climate_heating
                          hvac_mode: heat
              - if:
                  condition:
                    - lambda: 'return id(climate_heating_state).state == "heat";'
                  then:
                    - <<: *one_short
                    - homeassistant.service:
                        service: climate.set_hvac_mode
                        data:
                          entity_id: $ha_climate_heating
                          hvac_mode: auto
            on_long_press:
              - if:
                  condition:
                    - lambda: 'return id(climate_heating_state).state == "off";'
                  then:
                    - <<: *one_short
                    - homeassistant.service:
                        service: climate.set_hvac_mode
                        data:
                          entity_id: $ha_climate_heating
                          hvac_mode: auto
                  else:
                    - <<: &two_short
                        rtttl.play: 'three_short:d=4,o=5,b=100:16e6,16e6'
                    - homeassistant.service:
                        service: climate.set_hvac_mode
                        data:
                          entity_id: $ha_climate_heating
                          hvac_mode: "off"
            widgets:
              - label:
                  id: icon_climate_heating_1
                  <<: *icon_32_config
                  
        # '''''''''''''''''''''''''''''''''' current temperature
        - label:
            id: label_climate_heating_current_1
            text: ""
            y: 70
            text_font: montserrat_26

    # _______________________________________________________ page alarm
    - id: page_alarm
      skip: true
      <<: *page_config
      widgets:

        # '''''''''''''''''''''''''''''''''''''''''''' text zone
        - obj:
            id: alarm_code
            align: TOP_MID
            y: 14
            width: 100
            height: 25
            bg_color: text_color
            radius: 5
            widgets:
              - label:
                  id: label_alarm_code
                  align: CENTER
                  text_align: CENTER
                  text: "Code + \uF00C"
                  text_color: sec_color
                  
        # ''''''''''''''''''''''''''''''''''''''''''''''' keypad
        - btnmatrix:
            id: keypad_alarm
            align: TOP_MID
            y: 35
            width: 260
            height: 210
            border_opa: TRANSP
            bg_opa: TRANSP
            items:
              bg_color: pri_color
              text_color: text_color
              shadow_opa: TRANSP
              pressed:
                bg_color: nova_color
                text_color: sec_color
            one_checked: true
            rows:
              - buttons:
                  - <<: &hidden
                      text: 0
                      width: 1
                      control:
                        hidden: true
                  - text: 1
                    <<: &keybutton
                      width: 2
                      control:
                        no_repeat: true
                        recolor: true
                  - text: 2
                    <<: *keybutton
                  - text: 3
                    <<: *keybutton
                  - <<: *hidden
              - buttons:
                  - text: 4
                    <<: *keybutton
                  - text: 5
                    <<: *keybutton
                  - text: 6
                    <<: *keybutton
                  - text: 7
                    <<: *keybutton
              - buttons:
                  - <<: *hidden
                  - text: 8
                    <<: *keybutton
                  - text: 9
                    <<: *keybutton
                  - text: 0
                    <<: *keybutton
                  - <<: *hidden
              - buttons:
                  - <<: *hidden
                    width: 2
                  - text: "#de455c \uF00D#"  # red
                    key_code: "*"
                    <<: *keybutton
                  - text: "#a6da95 \uF00C#"  # green
                    key_code: "#"
                    <<: *keybutton
                  - <<: *hidden
                    width: 2
<<: !include m5dial_files/colors_and_fonts.yaml
color:
  # Nova Colors
  - id: nova_color # pink
    hex: "e38de3"
  - id: dark_nova_color # pink
    hex: "764a76"

  - id: pri_color # light (mantle)
    hex: "24273a"
  - id: sec_color # dark (crust)
    hex: "181926"

  - id: text_color
    hex: "cad3f5"
  - id: off_color
    hex: "5b6078"

  # Color Colors
  - id: blue_color
    hex: "6b93e3"
  - id: dark_blue_color
    hex: "344b79"

  - id: green_color
    hex: "a6da95"
  - id: dark_green_color
    hex: "374825"

  - id: yellow_color
    hex: "ffd17a"

  - id: orange_color
    hex: "e0752b"
  - id: dark_orange_color
    hex: "5f3e25"

  - id: red_color
    hex: "de455c"
  - id: dark_red_color
    hex: "642b2b"

font:
  - id: roboto_16_bold
    file:
      type: gfonts
      family: Roboto
      weight: bold
    size: 16
    <<: &extras
      extras:
        - file: "fonts/materialdesignicons-webfont.ttf"
          glyphs: [
            "\U000F1999", # mdi:rotate-360
            "\U000F0741", # mdi:gesture-tap
            "\U000F0046", # mdi:arrow-down-thick
            "\U000F0594", # clear-night
            "\U000F0590", # cloudy
            "\U000F0F2F", # exceptional
            "\U000F0591", # fog
            "\U000F0592", # hail
            "\U000F0593", # lightning
            "\U000F067E", # lightning-rainy
            "\U000F0595", # partlycloudy
            "\U000F0596", # pouring
            "\U000F0597", # rainy
            "\U000F0598", # snowy
            "\U000F067F", # snowy-rainy
            "\U000F0599", # sunny
            "\U000F059D", # windy
            "\U000F059E", # windy-variant
            "\U000F14E4", # sunny-off
            "\U000F073A", # mdi:cancel
            "\U000F0425", # mdi:power
            "\U000F0238", # mdi:fire
            "\U000F1B17", # mdi:thermostat-auto
            "\U000F0AD8", # mdi:radiator-off
            "\U000F0299", # mdi:gate
            "\U000F116A", # mdi:gate-open
            "\U000F17F8", # mdi:gate-alert
            "\U000F0E8B", # mdi:boom-gate-outline
            "\U000F0E87", # mdi:boom-gate-alert
            "\U000F10AF", # mdi:door-closed-lock
            "\U000F081C", # mdi:door-open
            "\U000F11DB", # mdi:window-closed-variant
            "\U000F11DC", # mdi:window-open-variant
            "\U000F12D3", # mdi:garage-variant
            "\U000F12D4", # mdi:garage-open-variant
            "\U000F06E8", # mdi:lightbulb-on
            "\U000F0336", # mdi:lightbulb-outline
            ]
  - id: roboto_20_regular
    file:
      type: gfonts
      family: Roboto
      weight: regular
    size: 20
    <<: *extras
  - id: roboto_20_bold
    file:
      type: gfonts
      family: Roboto
      weight: bold
    size: 20
    <<: *extras
  - id: roboto_24_regular
    file: 
      type: gfonts
      family: Roboto
      weight: regular
    size: 24
    <<: *extras
  - id: roboto_24_bold
    file: 
      type: gfonts
      family: Roboto
      weight: bold
    size: 24
    <<: *extras
  - id: roboto_48_bold
    file: 
      type: gfonts
      family: Roboto
      weight: bold
    size: 48
    <<: *extras


  - id: grandstander_32_regular
    file: 
      type: gfonts
      family: Grandstander
      weight: regular
    size: 32
    <<: *extras
  - id: grandstander_36_regular
    file: 
      type: gfonts
      family: Grandstander
      weight: regular
    size: 36
    <<: *extras
  - id: grandstander_40_regular
    file: 
      type: gfonts
      family: Grandstander
      weight: regular
    size: 40
    <<: *extras
1 « J'aime »

J’ai ce bidule sur mon bureau depuis… deux mois ? Malheureusement, si tout le reste (tactile, encodeur, rétro-éclairage…) fonctionne, je n’arrive pas du tout à faire fonctionner l’écran en lui-même avec ESPHome. C’est un ST7701S qui est supporté en théorie, mais je ne parviens pas à le configurer correctement… Le M5Stack Dial est sans doute une option plus sûre pour le moment, même si plus petite !

salut j’ai bien aimer ton travail super moi de mon coter j’essaye de mettre en place alarmo est je galere