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)
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
blackwhen within a healthy range, but automatically switch toredto 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!
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 %}