Thanks for the feedback! Which version are you using! The latest v2.0.3 brings some interesting performance improvements
Updated to v2.0.3 and will retest. Feedback will follow soon.
Is the onchange event on an input element that is used with a drag not going to fire every change it sees? In other words, if I drag the slider from 0 to 255, is this going to hit the event 255 times and thus call the Hass service 255 times? Can we use something like focused?
Change event fires only when value has been committed (see here: HTMLElement: change event - Web APIs | MDN). In the case of a range input, only when you release.
@usernein This is awesome. This would make styling custom cards a breeze. Iâll give it a try in the next few days and report back.
One immediate concern or comment I have is embedding logic (JavaScript) inside the content itself. It would be nice if there was a way to have the chance to specify a script that could handle all the interaction with the HTML content youâre rendering.
I found this (New lovelace custom card approach - fully customizable card with your own design) a few minutes ago, where thereâs a clear separation between content, binding and behavior. I donât necessarily think binding is needed, but scripts (behavior) definitely is when you start working on more complex interactive cards.
I know this might go beyond the âstyle cards using Tailwindâ approach shown here, but Iâd love to see a solution to create custom cards in a more lightweight manner instead of creating a full-blown custom component.
Let me know what you think.
Wow, this is absolutely awesome.
Is there any way to use the Home Assistant SVG icons (MDI for example)?
For example, you can just use this inside content:
<ha-icon icon="mdi:ceiling-fan-light"></ha-icon>
@silvio we are on the same page! I
Thatâs exactly what i did on the first example: call a service on home assistant passing the value of the input. You just need to adjust it to use the home assistant service that you want.
But i did the work for you (and tested):
<div class="card w-full bg-base-100 shadow-xl">
<figure><img src="https://picsum.photos/300/100" alt="Keuken" /></figure>
<div class="card-body">
<h2 class="card-title">
Banheiro
<div class="badge badge-secondary"> {{ 'fout' if is_state('light.banheiro', 'unavailable') else ''}} </div>
</h2>
<p>This is a light card wip</p>
<div class="card-actions">
<button class="btn btn-wide {{ 'btn-success' if is_state('light.banheiro', 'on') else ''}}" onClick="hass.callService('homeassistant', 'toggle', {entity_id: 'light.banheiro'})">{{ 'aan' if is_state('light.banheiro', 'on') else 'uit'}}</button>
<div class="flex">
<div class="flex-auto w-64">
<input type="range" min="0" max="255" value={{ state_attr('light.banheiro', 'brightness') }} class="range range-lg pr-10" onChange="hass.callService('light', 'turn_on', {'entity_id': 'light.banheiro', brightness: this.value})"/>
</div>
<div class="flex-auto w-32">
<input type="checkbox" class="toggle toggle-success toggle-lg" {{ 'checked' if is_state('light.banheiro', 'on') else 'unchecked'}} onClick="hass.callService('homeassistant', 'toggle', {entity_id: 'light.banheiro'})" />
</div>
</div>
</div>
<div class="card-actions justify-end">
<div class="badge badge-outline">{{expand('light.banheiro')|selectattr('state','eq','on')|list|count}} / {{expand('light.lichten_keuken')|list|count}}</div>
<div class="badge badge-outline">
{{states('light.banheiro')}}
{% if(is_number(state_attr('light.banheiro', 'brightness')))%}
{% set bri= state_attr('light.banheiro', 'brightness')%}
{% set bri = (bri/2.55)|round(0)%}
{%else%}
{% set bri=0%}
{%endif%}
{{bri}}
%</div>
</div>
</div>
</div>
Note that i changed the input range to 0-255
@silvio About onchange, it will run only on release, as @fabianluque said. If you want to update instantly, use âonInputâ, but be aware that if you use jinja templates to fetch entities values, the card will update everytime the entities update
@fabianluque i took a while to fully understand how amazing the approach of the project you shared is
I totally agree with you. I was already dissapointed on how long complex cards as the last examples i shared here can be. Specially when you need to write the same entity_id again and again or when you need to have the card synced with the state.
I was even thinking about using aliases for âhass.callServiceâ
I got excited by the idea, so i can confirm that you will see this amazing feature very soon.
What I do to solve the issue of having to rewrite the hass element over and over again is the following. I define my light and other checks as illustrated below:
{{% set lichten = 'light.lichten_keuken' %}
{% set attr_bri = 'brightness' in state_attr(lichten, 'supported_color_modes') %}
<div class="card w-full bg-base-100 shadow-xl">
<div class="bg-cover bg-center h-56 p-4" style="background-image: url(/local/images/ui_elements/lamp-kort.png)">
<div class="flex justify-end">
<ha-icon icon="mdi:ceiling-fan-light"></ha-icon>
<svg class="h-6 w-6 text-black fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M12.76 3.76a6 6 0 0 1 8.48 8.48l-8.53 8.54a1 1 0 0 1-1.42 0l-8.53-8.54a6 6 0 0 1 8.48-8.48l.76.75.76-.75zm7.07 7.07a4 4 0 1 0-5.66-5.66l-1.46 1.47a1 1 0 0 1-1.42 0L9.83 5.17a4 4 0 1 0-5.66 5.66L12 18.66l7.83-7.83z"></path>
</svg>
<div class="relative">
<div class="w-16 h-16 bg-white rounded-lg shadow-2xl"></div>
<div class="absolute top-0 right-0 -mr-1 -mt-1 w-4 h-4 rounded-full bg-green-300 animate-ping"></div>
<div class="absolute top-0 right-0 -mr-1 -mt-1 w-4 h-4 rounded-full bg-green-300"></div>
</div>
</div>
</div>
<div class="card-body">
<h2 class="card-title">
KEUKEN
<div class="badge {{ 'badge-secondary' if is_state(lichten, 'unavailable') else 'badge-success'}}"> {{ 'fout' if is_state(lichten, 'unavailable') else 'ok'}} </div>
</h2>
<p> {% if (expand(lichten)|selectattr('state','in',['unavailable','unknown','none'])|list|count) ==0 %}
Alle lampen zijn ok.
{% else%}
{{ expand(lichten)|selectattr('state','in',['unavailable','unknown','none'])|list|map(attribute='name')|join('\n- ')}}
{%endif%}
</p>
<div class="card-actions">
<button class="btn btn-wide {{ 'btn-success' if is_state(lichten, 'on') else ''}}" onClick="hass.callService('homeassistant', 'toggle', {entity_id: lichten})">{{ 'aan' if is_state(lichten, 'on') else 'uit'}}</button>
<div class="flex">
<div class="flex-auto w-64 {% if (attr_bri==false)%}hidden{%endif%}">
<input type="range" min="0" max="255" value="{% if(is_number(state_attr(lichten, 'brightness')))%}{% set bri= state_attr(lichten, 'brightness')|round(0)%}{%else%}{% set bri=0%}{%endif%}{{bri|round(0)}}" class="range range-lg pr-10" onchange="hass.callService('light', 'turn_on', { entity_id: lichten, brightness: this.value })" class="range" step="25" />
</div>
<div class="flex-auto w-32">
<input type="checkbox" class="toggle toggle-success toggle-lg" {{ 'checked' if is_state('automation.keuken_pir_beweging', 'on') else 'unchecked'}} onClick="hass.callService('homeassistant', 'toggle', {entity_id: 'automation.keuken_pir_beweging'})" />
</div>
</div>
</div>
<div class="card-actions justify-end">
<div class="badge badge-outline">{{expand(lichten)|selectattr('state','eq','on')|list|count}} / {{expand(lichten)|list|count}}
</div>
<div class="badge badge-outline">
{{states(lichten)}}
{% if(is_number(state_attr(lichten, 'brightness')))%}
{% set bri= state_attr(lichten, 'brightness')%}
{% set bri = (bri/2.55)|round(0)%}
{%else%}
{% set bri=0%}
{%endif%}
{{bri}}
%</div>
</div>
</div>
<div class="collapse bg-base-200">
<input type="checkbox" />
<div class="collapse-title text-xl font-medium">
Click me to show/hide content
</div>
<div class="collapse-content">
<p>hello</p>
</div>
</div>
</div>
Next nice feature would be to be able to use the hass icons locally. As they already exist in the system, we donât want to pull them over and over from the cloud. Can this be done?
@silvio About the icons: TailwindCSS Template Card - build custom cards with HTML and TailwindCSS - #27 by fabianluque
Tx for the help guys.
Could you have a look at this? Maybe you can determine why itâs not running? It doesnât recognize the variables.
{% set lichten = 'light.lichten_keuken' %}
{% set attr_bri = 'brightness' in state_attr(lichten, 'supported_color_modes') %}
<div class="card w-full bg-base-100 shadow-xl">
<div class="bg-cover bg-center h-56 p-4" style="background-image: url(/local/images/ui_elements/lamp-kort.png)">
<div class="flex justify-end">
<ha-icon icon="mdi:ceiling-fan-light"></ha-icon>
<svg class="h-6 w-6 text-black fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M12.76 3.76a6 6 0 0 1 8.48 8.48l-8.53 8.54a1 1 0 0 1-1.42 0l-8.53-8.54a6 6 0 0 1 8.48-8.48l.76.75.76-.75zm7.07 7.07a4 4 0 1 0-5.66-5.66l-1.46 1.47a1 1 0 0 1-1.42 0L9.83 5.17a4 4 0 1 0-5.66 5.66L12 18.66l7.83-7.83z"></path>
</svg>
<div class="relative">
<div class="w-16 h-16 bg-white rounded-lg shadow-2xl"></div>
<div class="absolute top-0 right-0 -mr-1 -mt-1 w-4 h-4 rounded-full bg-green-300 animate-ping"></div>
<div class="absolute top-0 right-0 -mr-1 -mt-1 w-4 h-4 rounded-full bg-green-300"></div>
</div>
</div>
</div>
<div class="card-body">
<h2 class="card-title">
KEUKEN
<div class="badge {{ 'badge-secondary' if is_state(lichten, 'unavailable') else 'badge-success'}}"> {{ 'fout' if is_state(lichten, 'unavailable') else 'ok'}} </div>
</h2>
<p> {% if (expand(lichten)|selectattr('state','in',['unavailable','unknown','none'])|list|count) ==0 %}
Alle lampen zijn ok.
{% else%}
{{ expand(lichten)|selectattr('state','in',['unavailable','unknown','none'])|list|map(attribute='name')|join('\n- ')}}
{%endif%}
</p>
<div class="card-actions">
<button class="btn btn-wide {{ 'btn-success' if is_state(lichten, 'on') else ''}}" onClick="hass.callService('homeassistant', 'toggle', {entity_id: lichten})">{{ 'aan' if is_state(lichten, 'on') else 'uit'}}</button>
<div class="flex">
<div class="flex-auto w-64 {% if (attr_bri==false)%}hidden{%endif%}">
<input type="range" min="0" max="255" value="{% if(is_number(state_attr(lichten, 'brightness')))%}{% set bri= state_attr(lichten, 'brightness')|round(0)%}{%else%}{% set bri=0%}{%endif%}{{bri|round(0)}}" class="range range-lg pr-10" onchange="hass.callService('light', 'turn_on', { entity_id: lichten, brightness: this.value })" class="range" step="25" />
</div>
<div class="flex-auto w-32">
<input type="checkbox" class="toggle toggle-success toggle-lg" {{ 'checked' if is_state('automation.keuken_pir_beweging', 'on') else 'unchecked'}} onClick="hass.callService('homeassistant', 'toggle', {entity_id: 'automation.keuken_pir_beweging'})" />
</div>
</div>
</div>
<div class="card-actions justify-end">
<div class="badge badge-outline">{{expand(lichten)|selectattr('state','eq','on')|list|count}} / {{expand(lichten)|list|count}}
</div>
<div class="badge badge-outline">
{{states(lichten)}}
{% if(is_number(state_attr(lichten, 'brightness')))%}
{% set bri= state_attr(lichten, 'brightness')%}
{% set bri = (bri/2.55)|round(0)%}
{%else%}
{% set bri=0%}
{%endif%}
{{bri}}
%</div>
</div>
</div>
<div class="collapse bg-base-200">
<input type="checkbox" />
<div class="collapse-title text-xl font-medium">
Click me to show/hide content
</div>
<div class="collapse-content">
<p>hello</p>
</div>
</div>
</div>
I found my mistake. Lichten cannot be called from onclick as itâs not a known var in th js scope. Rookie mistake.
Despite correcting the issue by calling the entity by the entity_id known in Hass, I still have many freezes. After loading the view with the tailwind UI the elements donât appear. Very often it doesnât load the objects at all, but the native Hass objects are loaded fine in the view.
When the tailwind components are loaded correctly in the view, the buttons to toggle my light (onclick) donât respond at all. Itâs strange because after reboot the onclick is fast as native (very rare), after a few clicks I have delays going up to 10 seconds but more often I have no response at all. This what my log shows:
- blob:https://something.ui.nabu.casa/somekey:1:1 Uncaught NetworkError: Failed to execute âimportScriptsâ on âWorkerGlobalScopeâ: The script at âhttps://something.ui.nabu.casa/lovelace/function%20Ou(){let%20s;try{if(s=Ao&&(window.URL||window.webkitURL).createObjectURL(Ao),!s)throw%22%22;return%20new%20Worker(s)%7Dcatch%7Breturn%20new%20Worker(%22data:application/javascript;base64,%22+gs)%7Dfinally%7Bs&&(window.URL%7C%7Cwindow.webkitURL).revokeObjectURL(s)%7D%7Dâ failed to load.
Hey @fabianluque you can already test the Bindings on the beta version v2.1.0-1
, which is already downloadable through HACS (as a beta version).
Although i wrote it from scratch in TypeScript, it works on the exact same way of the approach you shared
Iâm not able to download using HACS, compared to other versions seems the .js file is missing in the assets of the release.
Same here.
My fault, sorry! Can you try again with v2.1.0-7
?
Great, now it worked.
Did a quick test with a binding, works for first render but if state is updated it doesnât refresh. Hereâs a sample configuration:
- type: custom:tailwindcss-template-card
entity: light.attic_lights
bindings:
- bind: return entity.state
selector: span
type: text
content: |
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
Attic Lights State: <span></span>
</button>
This is great progress! Thanks so much for going ahead and implementing this idea, really appreciated.