I put together a soil moisture visualization based on my new Ecowitt soil moisture sensors in four locations around the house.
Each sensor gets a little fish bowl visualization, and the background color of each “bowl” changes to make dry plants apparent from across the room:
Code:
type: custom:tailwindcss-template-card
entity: ""
content: |-
<div class="rounded-xl shadow-lg p-6 max-w-md mx-auto">
<h2 class="text-xl font-bold mb-6 text-center">Soil Moisture</h2>
<div class="grid grid-cols-4 gap-4">
{% set plants = [
{"name": "Dracaena", "sensor": "sensor.ecowitt_bridge_gw1200b_soil_moisture_3", "emoji": "🌴"},
{"name": "Diffenbachia", "sensor": "sensor.ecowitt_bridge_gw1200b_soil_moisture_2", "emoji": "🌿"},
{"name": "Monstera", "sensor": "sensor.ecowitt_bridge_gw1200b_soil_moisture_4", "emoji": "🧌"},
{"name": "Snake", "sensor": "sensor.ecowitt_bridge_gw1200b_soil_moisture_1", "emoji": "🐍"},
] %}
{% for plant in plants %}
{% set moisture = states(plant.sensor) | float %}
{% set fill_percentage = moisture / 100 %}
{% set water_level = 93 - (86 * fill_percentage) %}
{% set ellipse_rx = (43*43 - (water_level-50)*(water_level-50))**0.5 %}
{% set view_angle = (water_level - 50) / 43 %}
{% set ellipse_ry = 2 + (13 * (1 - view_angle**2)**0.5) %}
{% if moisture < 5 %}
{% set color = "red-500" %}
{% elif moisture < 10 %}
{% set color = "orange-500" %}
{% elif moisture < 20 %}
{% set color = "orange-300" %}
{% else %}
{% set color = "emerald-100" %}
{% endif %}
<div class="flex flex-col items-center">
<div class="relative w-24 h-24 mb-3">
<svg class="w-full h-full" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="45" class="fill-{{color}} stroke-gray-400" stroke-width="2"/>
<defs>
<clipPath id="circle-clip-{{loop.index}}">
<circle cx="50" cy="50" r="43"/>
</clipPath>
<clipPath id="ellipse-top-{{loop.index}}">
<rect x="0" y="0" width="100" height="{{water_level}}"/>
</clipPath>
</defs>
<rect
x="7"
y="{{water_level}}"
width="86"
height="{{86 * fill_percentage}}"
class="fill-blue-700/30 transition-all duration-700 ease-in-out"
clip-path="url(#circle-clip-{{loop.index}})"
/>
{% if fill_percentage > 0.02 %}
<ellipse
cx="50"
cy="{{water_level}}"
rx="{{ellipse_rx}}"
ry="{{ellipse_ry}}"
class="fill-blue-500/40"
clip-path="url(#ellipse-top-{{loop.index}})"
/>
{% endif %}
<text x="50" y="55" text-anchor="middle" dominant-baseline="middle" class="text-6xl">{{plant.emoji}}</text>
<rect
x="7"
y="{{water_level}}"
width="86"
height="{{86 * fill_percentage}}"
class="fill-blue-700/50 transition-all duration-700 ease-in-out"
clip-path="url(#circle-clip-{{loop.index}})"
/>
</svg>
</div>
<h3 class="font-medium text-sm text-center">{{plant.name}}</h3>
<p class="text-xl font-bold text-{{color}}">{{moisture|round}}%</p>
</div>
{% endfor %}
</div>
</div>
ignore_line_breaks: true
always_update: false
parse_jinja: true
code_editor: Ace
debounceChangePeriod: 100
plugins:
daisyui:
enabled: true
url: https://cdn.jsdelivr.net/npm/daisyui@latest/dist/full.css
theme: dark - dark
overrideCardBackground: false
tailwindElements:
enabled: false
