Keep Jinja inside the quotes
The template renders first, then the result is parsed as JSON. So the Jinja has to sit
inside the string it produces, not around the key.
✗ "icon": {% if hot %}"mdi:fire"{% endif %}
✓ "icon": "{% if hot %}mdi:fire{% else %}mdi:snowflake{% endif %}"
Numbers and booleans go outside
Values that aren't strings (value, pie_fill,
card_border_radius, fill_area) are emitted bare. Jinja prints
True, which looks wrong but is fine: Home Assistant evaluates the result as a Python
literal before the card sees it.
✓ "value": {{ state | float(0) }},
"fill_area": {{ is_state('input_boolean.fill','on') }}
state is an input, state_text is an output
{{ state }} is the entity's value coming in. The property that sets the text a module
displays is state_text. Returning state also works (it's accepted as an
alias because everyone tries it), but the others have no such alias, so a typo just does nothing.
Unknown keys are flagged under the editor.
Hiding things is a different switch
A module template controls how a module looks, never whether it appears. To show and hide,
use Display Conditions on the module, or Template Mode on the row or column and return
visible.
Name entities with states()
Home Assistant watches the entities your template mentions and re-renders only when they change.
Reading states('sensor.x') lets it track that for you. The snapshot variables
(state, attributes) are handed in per render instead, so Ultra Card has to
resubscribe when they move. Both are correct, but states() is the lighter path for
wall tablets.
Reuse values with $variables
Define a variable once in the Hub and drop $living_room_temp into any template; Ultra
Card substitutes it before Home Assistant renders. Need a literal dollar sign? Write
$$.