Quick share: cute soil moisture card with TailwindCSS

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
2 Likes

I love it! I just ordered a couple of soil moisture sensors and this makes me excited to start playing around with them :smile: