Aquarium, Pool & Hydroponics Dashboard (7.4" BWR) ‐ pH, EC, Temp with History Plots - OpenEPaperLink/Home_Assistant_Integration GitHub Wiki

Aquarium, Pool & Hydroponics Dashboard (7.4" BWR)

PXL_20260409_062505638

This layout is specifically designed for 7.4" BWR (Black/White/Red) E-Paper Displays (Resolution: 640x384). It acts as a comprehensive dashboard for monitoring water parameters like pH, EC (Electrical Conductivity), and Water Temperature, making it perfect for aquariums, hydroponic setups, or pools.

✨ Features

  • Symmetrical 3-Grid Layout: Clean separation of all three values.
  • 24h History Plots: Native ATC-Plots including X/Y-axis labels and automated scaling.
  • Dynamic Colors: Values and graphs are drawn in black when within a healthy range, but automatically switch to red to alert you if they drop or spike out of bounds.
  • Auto Dark Mode: Switch between a white and a black background. All lines and text elements will automatically invert their colors to stay visible!
  • Advanced Settings: Easily configure Screen Rotation (0°/180°), Time-to-Live (TTL), and Dithering.
  • Reliable Screen Updates: Uses clean Full-Refreshes to perfectly drive the 3-color (BWR) particles on the large 7.4" screen without any ghosting artifacts.

🚀 Easy Installation: Blueprint (Recommended)

The easiest way to use this dashboard is by importing the Blueprint. It provides a clean UI in Home Assistant where you can simply select your E-Paper display, your sensors, and customize your red/black warning thresholds.

Furthermore, the Blueprint allows you to toggle Dark Mode, set the Display Rotation, adjust the TTL (Time to Live), and choose between a dot (.) or a comma (,) as the decimal separator!

Open your Home Assistant instance and show the blueprint import dialog with a specific blueprint pre-filled.

Fallback Gist URL: https://gist.github.com/z9m/2132afc6926c3443bc13a3479f751321


🔄 Update Interval & Screen Refresh Modes

Updating a large 7.4" 3-color display requires a Full Refresh to properly move the heavy red pigments and prevent ghosting (the Chroma74 firmware actually enforces this automatically). To ensure your batteries (6x CR2450) last for a very long time, this automation defaults to a 30-minute update interval (minutes: "/30").

Using Black/White (BW) or compatible displays? If you are adapting this Blueprint for a standard BW panel or a display that supports partial updates, you can fully utilize the built-in Smart Refresh modes! These modes use battery-saving, flash-free partial updates throughout the day. To prevent the inevitable e-paper ghosting, the Blueprint automatically triggers a single full refresh exactly at 3:00 AM in the night to "wash" the screen clean.


🛠️ Manual Installation (YAML Code)

If you prefer not to use the Blueprint (which includes all features like Dark Mode and Rotation), you can manually create the standard Light Mode automation using the YAML code below.

🇬🇧 English Version

Uses English labels and dot (.) as decimal separator.

How to use: Create a new blank automation in Home Assistant, switch to Edit in YAML mode, paste the code below, and replace the placeholders (YOUR_E_PAPER_DEVICE_ID_HERE and sensor.YOUR_...) with your actual entities.

alias: "E-Paper: Aquarium / Hydroponics Dashboard"
description: "Updates every 30 mins (Partial). Does 1x Full Refresh at 3:00 AM to prevent ghosting."
mode: restart

trigger:
  - platform: time_pattern
    minutes: "/30"

action:
  - action: open_epaper_link.drawcustom
    target:
      device_id: YOUR_E_PAPER_DEVICE_ID_HERE
    data:
      background: white
      rotate: 0
      dither: "0"
      ttl: 60
      dry-run: false
      
      # Smart Refresh: "0" (Full) at 03:00 AM, "2" (Partial) otherwise
      refresh_type: "{{ '0' if now().hour == 3 and now().minute == 0 else '2' }}"
      
      payload:
        # === OPTICAL DIVIDER LINES ===
        - type: line
          x_start: 0
          y_start: 192
          x_end: 640
          y_end: 192
          width: 2
          color: black
        - type: line
          x_start: 320
          y_start: 0
          x_end: 320
          y_end: 192
          width: 2
          color: black

        # ==========================================
        # === 1. pH VALUE (TOP LEFT) ===
        # ==========================================
        - type: text
          value: "pH Value"
          font: "ppb.ttf"
          x: 160
          y: 10
          anchor: mt
          size: 25
          color: black
          
        - type: text
          value: "{{ '%.2f' | format(states('sensor.YOUR_PH_SENSOR')|float(0)) }}"
          font: "ppb.ttf"
          x: 160
          y: 75
          anchor: mm
          size: 65
          # Color Logic pH: Black if in normal range (5.2 to 6.8), else Red
          color: >
            {% set ph = states('sensor.YOUR_PH_SENSOR') | float(0) %}
            {% if 5.2 <= ph <= 6.8 %} black
            {% else %} red {% endif %}
            
        - type: plot
          x_start: 40           
          y_start: 120
          x_end: 280            
          y_end: 175            
          duration: 86400
          font: "ppb.ttf"
          ylegend:
            width: 35           
            size: 11            
            color: black
          yaxis:
            tick_every: 2
            color: grey         
          xlegend:
            width: 20
            size: 10
            color: black
          xaxis:
            tick_every: 4
            color: grey
          data:
            - entity: sensor.YOUR_PH_SENSOR
              width: 2
              color: >
                {% set ph = states('sensor.YOUR_PH_SENSOR') | float(0) %}
                {% if 5.2 <= ph <= 6.8 %} black
                {% else %} red {% endif %}

        # ==========================================
        # === 2. EC VALUE (TOP RIGHT) ===
        # ==========================================
        - type: text
          value: "EC Value"
          font: "ppb.ttf"
          x: 480
          y: 10
          anchor: mt
          size: 25
          color: black
          
        - type: text
          value: "{{ '%.2f' | format(states('sensor.YOUR_EC_SENSOR')|float(0)) }}"
          font: "ppb.ttf"
          x: 480
          y: 75
          anchor: mm
          size: 65
          # Color Logic EC: Black if in normal range (0.4 to 2.2), else Red
          color: >
            {% set ec = states('sensor.YOUR_EC_SENSOR') | float(0) %}
            {% if 0.4 <= ec <= 2.2 %} black
            {% else %} red {% endif %}
            
        - type: plot
          x_start: 360          
          y_start: 120
          x_end: 600            
          y_end: 175
          duration: 86400
          font: "ppb.ttf"
          ylegend:
            width: 35
            size: 11
            color: black
          yaxis:
            tick_every: 0.5
            color: grey
          xlegend:
            width: 20
            size: 10
            color: black
          xaxis:
            tick_every: 4
            color: grey
          data:
            - entity: sensor.YOUR_EC_SENSOR
              width: 2
              color: >
                {% set ec = states('sensor.YOUR_EC_SENSOR') | float(0) %}
                {% if 0.4 <= ec <= 2.2 %} black
                {% else %} red {% endif %}

        # ==========================================
        # === 3. TEMPERATURE (BOTTOM) ===
        # ==========================================
        - type: text
          value: "Water Temperature"
          font: "ppb.ttf"
          x: 320
          y: 205
          anchor: mt
          size: 25
          color: black
          
        - type: text
          value: "{{ '%.1f' | format(states('sensor.YOUR_TEMPERATURE_SENSOR')|float(0)) }} °C"
          font: "ppb.ttf"
          x: 320
          y: 260
          anchor: mm
          size: 50
          # Color Logic Temperature: Black if in normal range (15 to 31), else Red
          color: >
            {% set t = states('sensor.YOUR_TEMPERATURE_SENSOR') | float(0) %}
            {% if 15 <= t <= 31 %} black
            {% else %} red {% endif %}
            
        - type: plot
          x_start: 45           
          y_start: 295          
          x_end: 595
          y_end: 365            
          duration: 86400
          font: "ppb.ttf"
          ylegend:
            width: 40
            size: 12            
            color: black
          yaxis:
            tick_every: 3
            color: grey
          xlegend:
            width: 25
            size: 12
            color: black
          xaxis:
            tick_every: 4
            color: grey
          data:
            - entity: sensor.YOUR_TEMPERATURE_SENSOR
              width: 3
              color: >
                {% set t = states('sensor.YOUR_TEMPERATURE_SENSOR') | float(0) %}
                {% if 15 <= t <= 31 %} black
                {% else %} red {% endif %}

🇩🇪 Deutsche Version (German)

Nutzt deutsche Beschriftungen und das Komma (,) als Dezimaltrennzeichen.

🔄 Update-Intervall & Refresh-Modi

Ein großes 7.4" 3-Farben-Display benötigt physikalisch einen Full Refresh, um die schweren roten Pigmente sauber zu bewegen (die Firmware des Chroma74 erzwingt dies automatisch). Um die Batterien (6x CR2450) zu schonen, nutzt diese Blaupause standardmäßig ein Intervall von 30 Minuten.

Nutzt du ein Schwarz/Weiß (BW) Display? Falls du diese Blueprint für ein BW-Display nutzt, das Partial-Updates unterstützt, kannst du die integrierten Smart Refresh Modi nutzen! Diese updaten das Display tagsüber blitzschnell und batterieschonend im partial Modus (ohne Flackern). Um das typische E-Paper-Ghosting zu verhindern, erzwingt die Automatisierung dann exakt nachts um 03:00 Uhr einen einzigen full Refresh, um das Display wieder komplett "reinzuwaschen".

Anleitung: Erstelle eine neue Automatisierung in Home Assistant, wechsle oben rechts auf Als YAML bearbeiten, füge den Code ein und ersetze die Platzhalter (DEINE_E_PAPER_GERAETE_ID_HIER und sensor.DEIN_...) durch deine eigenen Entitäten.

alias: "E-Paper: Aquaristik / Hydroponik Dashboard"
description: "Update alle 30 Min (Partial). Um 03:00 Uhr nachts 1x Full Refresh gegen Ghosting."
mode: restart

trigger:
  - platform: time_pattern
    minutes: "/30"

action:
  - action: open_epaper_link.drawcustom
    target:
      device_id: DEINE_E_PAPER_GERAETE_ID_HIER
    data:
      background: white
      rotate: 0
      dither: "0"
      ttl: 60
      dry-run: false
      
      # Smart Refresh: "0" (Full) at 03:00 AM, "2" (Partial) otherwise
      refresh_type: "{{ '0' if now().hour == 3 and now().minute == 0 else '2' }}"
      
      payload:
        # === OPTISCHE TRENNLINIEN ===
        - type: line
          x_start: 0
          y_start: 192
          x_end: 640
          y_end: 192
          width: 2
          color: black
        - type: line
          x_start: 320
          y_start: 0
          x_end: 320
          y_end: 192
          width: 2
          color: black

        # ==========================================
        # === 1. pH-WERT (OBEN LINKS) ===
        # ==========================================
        - type: text
          value: "pH-Wert"
          font: "ppb.ttf"
          x: 160
          y: 10
          anchor: mt
          size: 25
          color: black
          
        - type: text
          value: "{{ ('%.2f' | format(states('sensor.DEIN_PH_SENSOR')|float(0))) | replace('.', ',') }}"
          font: "ppb.ttf"
          x: 160
          y: 75
          anchor: mm
          size: 65
          # Farb-Logik pH: Schwarz wenn im Normalbereich (5.2 bis 6.8), sonst Rot
          color: >
            {% set ph = states('sensor.DEIN_PH_SENSOR') | float(0) %}
            {% if 5.2 <= ph <= 6.8 %} black
            {% else %} red {% endif %}
            
        - type: plot
          x_start: 40           
          y_start: 120
          x_end: 280            
          y_end: 175            
          duration: 86400
          font: "ppb.ttf"
          ylegend:
            width: 35           
            size: 11            
            color: black
          yaxis:
            tick_every: 2
            color: grey         
          xlegend:
            width: 20
            size: 10
            color: black
          xaxis:
            tick_every: 4
            color: grey
          data:
            - entity: sensor.DEIN_PH_SENSOR
              width: 2
              color: >
                {% set ph = states('sensor.DEIN_PH_SENSOR') | float(0) %}
                {% if 5.2 <= ph <= 6.8 %} black
                {% else %} red {% endif %}

        # ==========================================
        # === 2. EC-WERT (OBEN RECHTS) ===
        # ==========================================
        - type: text
          value: "EC-Wert"
          font: "ppb.ttf"
          x: 480
          y: 10
          anchor: mt
          size: 25
          color: black
          
        - type: text
          value: "{{ ('%.2f' | format(states('sensor.DEIN_EC_SENSOR')|float(0))) | replace('.', ',') }}"
          font: "ppb.ttf"
          x: 480
          y: 75
          anchor: mm
          size: 65
          # Farb-Logik EC: Schwarz wenn im Normalbereich (0.4 bis 2.2), sonst Rot
          color: >
            {% set ec = states('sensor.DEIN_EC_SENSOR') | float(0) %}
            {% if 0.4 <= ec <= 2.2 %} black
            {% else %} red {% endif %}
            
        - type: plot
          x_start: 360          
          y_start: 120
          x_end: 600            
          y_end: 175
          duration: 86400
          font: "ppb.ttf"
          ylegend:
            width: 35
            size: 11
            color: black
          yaxis:
            tick_every: 0.5
            color: grey
          xlegend:
            width: 20
            size: 10
            color: black
          xaxis:
            tick_every: 4
            color: grey
          data:
            - entity: sensor.DEIN_EC_SENSOR
              width: 2
              color: >
                {% set ec = states('sensor.DEIN_EC_SENSOR') | float(0) %}
                {% if 0.4 <= ec <= 2.2 %} black
                {% else %} red {% endif %}

        # ==========================================
        # === 3. TEMPERATUR (UNTEN) ===
        # ==========================================
        - type: text
          value: "Wassertemperatur"
          font: "ppb.ttf"
          x: 320
          y: 205
          anchor: mt
          size: 25
          color: black
          
        - type: text
          value: "{{ ('%.1f' | format(states('sensor.DEIN_TEMPERATUR_SENSOR')|float(0))) | replace('.', ',') }} °C"
          font: "ppb.ttf"
          x: 320
          y: 260
          anchor: mm
          size: 50
          # Farb-Logik Temperatur: Schwarz wenn im Normalbereich (15 bis 31), sonst Rot
          color: >
            {% set t = states('sensor.DEIN_TEMPERATUR_SENSOR') | float(0) %}
            {% if 15 <= t <= 31 %} black
            {% else %} red {% endif %}
            
        - type: plot
          x_start: 45           
          y_start: 295          
          x_end: 595
          y_end: 365            
          duration: 86400
          font: "ppb.ttf"
          ylegend:
            width: 40
            size: 12            
            color: black
          yaxis:
            tick_every: 3
            color: grey
          xlegend:
            width: 25
            size: 12
            color: black
          xaxis:
            tick_every: 4
            color: grey
          data:
            - entity: sensor.DEIN_TEMPERATUR_SENSOR
              width: 3
              color: >
                {% set t = states('sensor.DEIN_TEMPERATUR_SENSOR') | float(0) %}
                {% if 15 <= t <= 31 %} black
                {% else %} red {% endif %}