Warranty usage tracker markdown card
Posted: Wed Oct 01, 2025 2:18 pm
Example is for the EC4300-H4 battery, adjust as needed.
Template helper sensor.foxess_warranty_years
Template helper sensor.foxess_warranty_throughput
Template helper sensor.foxess_warranty_SoH
Template helper sensor.foxess_warranty_cycles
Template sensor sensor.foxess_battery_cycles
+ a utility meter, daily, sensor.foxess_battery_cycles_today on sensor.foxess_battery_cycles.
It's done via helpers because I also use these cards on multiple dashboards, and as an android widget stack, so all I need to do is edit the helper and all the cards stay in sync.
Warranty years 12, started 2025-01-09. Warranty period consumed 6.28%.
Expiry 2037-01-08
Cycle life 6000, full cycles consumed 277.3 (4.6%), today 0.43, remaining 5722.7, total battery charge 4.37MWh, total battery discharge 4.48MWh.
Estimated expiry 2041-04-25
Warrantied throughput 59.6MWh, = 13.61kWh per day, actual 16.08kWh. Total throughput 4.423MWh (7.42%).
Estimated expiry 2035-03-03
Warrantied SoH 70.0%, battery SoH 100.0%, original capacity 16.58kWh, remaining 16.58kWh.
Code: Select all
type: markdown
content: |-
{{states('sensor.foxess_warranty_years')}}<br>
{{states('sensor.foxess_warranty_cycles')}}<br>
{{states('sensor.foxess_warranty_throughput')}}<br>
{{states('sensor.foxess_warranty_soh')}}
text_only: true
Template helper sensor.foxess_warranty_years
Code: Select all
{% set warranty_years = 12 %}
{% set warranty_days = warranty_years * 365.25 - 1 %}
{% set warranty_start = (strptime('2025-01-09', "%Y-%m-%d")|as_datetime).date() %}
{% set warranty_end = (warranty_start+timedelta(days= warranty_days)) %}
{% set warranty_days = (warranty_end - warranty_start) %} {# Not sure why this is needed, can I just use warrnaty_days without reassigning it #}
{% set days_since_warranty_start = (now().date() - warranty_start) %}
{% set perc_days_consumed = ((days_since_warranty_start / warranty_days) * 100) | round(2) %}
Warranty years <b>{{ warranty_years }}</b>, started {{warranty_start}}. Warranty period consumed <b>{{ perc_days_consumed }}</b>%.<br>Expiry <b>{{ warranty_end }}</b>
Code: Select all
{% set warranty_cycle_limit = 6000 %}
{% set warranty_throughput = 14900.0 * 4 %}
{% set battery_discharge_total = states('sensor.battery_discharge_total') | float %}
{% set battery_charge_total = states('sensor.battery_charge_total') | float %}
{% set warranty_start = (strptime('2025-01-09', "%Y-%m-%d")|as_datetime).date() %}
{% set throughput = ((battery_discharge_total + battery_charge_total)/2) %}
{% set throughput_perc = throughput / warranty_throughput * 100.0 %}
{% set days_since_warranty_start = (now().date() - warranty_start) %}
{% set throughput_per_day = throughput / (days_since_warranty_start.days) %}
{% set estimated_expiry_days = ((days_since_warranty_start / (throughput_perc / 100.0)).days )|int %}
{% set warranty_end = (warranty_start+timedelta(days= estimated_expiry_days)) %}
Warrantied throughput <b>{{(warranty_throughput/1000)|round(2)}}</b>MWh, = {{(warranty_throughput/12/365)|round(2)}}kWh per day, actual <b>{{throughput_per_day|round(2)}}</b>kWh. Total throughput <b>{{((battery_discharge_total + battery_charge_total)/2000
)|round(3)}}</b>MWh (<b>{{throughput_perc|round(2)}}</b>%). <br>Estimated expiry <b>{{warranty_end}}</b>
Template helper sensor.foxess_warranty_SoH
Code: Select all
{%- set warranty_start = (strptime('2025-01-09', "%Y-%m-%d")|as_datetime).date() -%}
{% set warrantied_soh = 70.0 %}
{% set original_battery_nominal_capacity_kwh = 16.58 %}
{# set nominal_voltage_v = 230.4 #}
{# set nominal_capacity_ah = 72 #}
{% set remaining_battery_nominal_capacity_kwh = (states('sensor.bms_kwh_remaining') | float)%}
{% set battery_SoH_inverter = states('sensor.battery_soh') | float %}
{%- set SoH_from_remaining = (remaining_battery_nominal_capacity_kwh/original_battery_nominal_capacity_kwh) * 100.0 -%}
{%- if SoH_from_remaining < battery_SoH_inverter -%}
{# use the most pessimistic #}
{%- set battery_SoH_inverter = SoH_from_remaining -%}
{%- endif -%}
Warrantied SoH <b>{{warrantied_soh}}</b>%, battery SoH <b>{{battery_SoH_inverter|round(2)}}</b>%, original capacity <b>{{original_battery_nominal_capacity_kwh|round(2)}}</b>kWh, remaining <b>{{remaining_battery_nominal_capacity_kwh|round(2)}}</b>kWh
{%- if battery_SoH_inverter < 99.95 -%}
{%- set days_since_warranty_start = (now().date() - warranty_start) -%}
{%- set estimated_expiry_days = ((days_since_warranty_start / ((100.0-battery_SoH_inverter) / (100.0-warrantied_soh))).days )|int -%}
{%- set warranty_end = (warranty_start+timedelta(days= estimated_expiry_days)) -%}
<br>Estimated expiry <b>{{warranty_end}}</b>{% endif %}.
Template helper sensor.foxess_warranty_cycles
Code: Select all
{% set warranty_start = (strptime('2025-01-09', "%Y-%m-%d")|as_datetime).date() %}
{% set warranty_cycle_limit = 6000 %}
{% set days_since_warranty_start = (now().date() - warranty_start) %}
{# set warranty_days = 12 * 365.25 - 1 #}
{# set warranty_end = (warranty_start+timedelta(days= warranty_days)) #}
{# set warranty_days = (warranty_end - warranty_start) #}
{% set battery_discharge_total = states('sensor.battery_discharge_total') | float %}
{% set battery_charge_total = states('sensor.battery_charge_total') | float %}
{% set full_cycles_consumed = states('sensor.foxess_battery_cycles') | float %}
{% set full_cycles_consumed_today = states('sensor.foxess_battery_cycles_today') | float %}
{% set cycles_perc_consumed = (( full_cycles_consumed / warranty_cycle_limit) * 100.0)%}
{% set estimated_expiry_days = ((days_since_warranty_start / (cycles_perc_consumed / 100.0)).days )|int %}
{% set warranty_end = (warranty_start+timedelta(days= estimated_expiry_days)) %}
Cycle life <b>{{warranty_cycle_limit|int}}</b>, full cycles consumed <b>{{ full_cycles_consumed | round(1) }}</b> (<b>{{cycles_perc_consumed | round(1) }}</b>%), today <b>{{ full_cycles_consumed_today | round(2) }}</b>, remaining <b>{{ (warranty_cycle_limit - full_cycles_consumed)|round(1) }}</b>, total battery charge <b>{{(battery_charge_total/1000.0)|round(2)}}</b>MWh, total battery discharge <b>{{(battery_discharge_total/1000.0)|round(2)}}</b>MWh.<br>Estimated expiry <b>{{warranty_end}}</b>
Code: Select all
{% set remaining_battery_nominal_capacity = (states('sensor.bms_kwh_remaining') | float)%}
{% set battery_discharge_total = states('sensor.battery_discharge_total') | float * 1.075 %}
{% set battery_charge_total = states('sensor.battery_charge_total') | float %}
{% set full_cycles_consumed = ((battery_discharge_total+ battery_charge_total) / (remaining_battery_nominal_capacity * 2)) | round(2) %}
{{full_cycles_consumed}}
It's done via helpers because I also use these cards on multiple dashboards, and as an android widget stack, so all I need to do is edit the helper and all the cards stay in sync.