How best to model complex logic for dynamic prices?

I’ve switched to dynamic pricing and need additional logic to calculate the cost of charging my EV. Looping through hourly price-lists isn’t as straightforward as I thought. Here are the scenarios and options I’ve considered. Which approach is best, or is there a better one I’ve missed?

Scenario: EV usage with dynamic prices

  • Goal: Calculate the charge time, total cost, and average cost, then send both the input and output to Google Sheets and Pushover.
  • Input: Entity with a list of current dynamic prices (as an attribute), start/stop time and kWh usage during that time.
  • Future Use: Reuse the logic for similar scenarios, like when to turn on the dishwasher.

Options

  1. Template:
  • Solution: Use the template directly inside the service (sorry: action) or with a template entity in between.

  • Pros: Native to Home Assistant, integrates well with other logic.

  • Cons: Templates are a “presentation” approach and logic needs to be duplicated for Google Sheets and Pushover. Template is now 61-lines long and debugging is difficult. Also performance could become a concern I think.

Although this post is not about reviewing my code specifically, I'll add the code for the template-option, for those interested, click here to open.
{% set prices = state_attr('sensor.dynamic_average_prices', 'prices') %}

{% set start_time = as_timestamp(states("input_datetime.charge_start_time")) %}
{% set end_time = as_timestamp(states("input_datetime.charge_end_time")) %}
{% set total_time = end_time - start_time %}

{% set consumption = states("sensor.ev_cable_consumption")|float - states("input_number.ev_consumption_last_usage")|float %}

{% set ns = namespace(price_per_hour=0, total_cost=0) %}
{% set loop_ns = namespace(from_time = start_time, to_time = start_time) %}

{% for i in range(int(total_time // 3600) + 1) %}
  {% set start_hour = as_datetime(loop_ns.from_time).replace(minute=0, second=0, microsecond=0) %}
  {% set end_hour = start_hour + timedelta(hours=1) %}
  {% set loop_ns.to_time = min(end_hour.timestamp(), end_time) %}
  
  {% set time_charged = loop_ns.to_time - loop_ns.from_time %}
  {% set consumption_this_hour = consumption * (time_charged / total_time) %}
  
  {% for price in prices %}
    {% if as_timestamp(price.time) <= loop_ns.from_time < as_timestamp(price.time) + 3600 %}
      {% set ns.price_per_hour = price.price %}
    {% endif %}
  {% endfor %}
  
  {% set ns.total_cost = ns.total_cost + (ns.price_per_hour * consumption_this_hour) %}
  
  {% set loop_ns.from_time = loop_ns.to_time %}
{% endfor %}

{% set charge_time = (as_datetime(end_time) - as_datetime(start_time)) %}
{% set charge_time_formatted = (charge_time | string).split('.')[0] %}
{% set battery_before = states('input_number.battery_before_charge')|int %}
{% set battery_after = states('sensor.battery_sensor') %}
{% set avg_price = ns.total_cost / consumption %}

{{ battery_before }}% -> {{ battery_after }}% | Consumption: {{ '{:.1f}'.format(consumption) }} kWh | Cost: €{{ '{:.2f}'.format(ns.total_cost) }} | Average price: €{{ '{:.2f}'.format(avg_price) }} | Charge time: {{ charge_time_formatted }} charged


  1. Script:
  • Solution: I haven’t got this working yet, but it should be possible to loop through the prices (repeat / count) and do the calculations inside a script.
  • Pros: Separates calculation from presentation (save variables to input_numbers and other helpers). Integrated well in HA, usage in automations for example.
  • Cons: Programming in YAML is complex, hard to read and error-prone and there are no good debugging options. 100-line script still doesn’t work.
  1. Python Script:
  • Solution: Not tried yet.
  • Pros: Simplifies complex logic using regular Python, easier to read and write. More powerful and flexible for advanced calculations.
  • Cons: Seperation also makes it less visible from HA. Few references to this approach. Is this less common?
  1. AppDaemon:
  • Solution: Used this in the past, much more powerful to create seperated and complex logic.
  • Pros: Solves programming issues of the first two options, more powerful scripting environment.
  • Cons: Adds complexity with interaction between AppDaemon and HA, somewhat hidden from HA: I tend to forget about things I did in AppDaemon…
  1. Custom Component:
  • Solution: Why not go all in?
  • Pros: Clean solution, solving many described problems, potential for reusability and extensibility. Integrates well. Could become a dynamic price calculator component if more scenarios are added.
  • Cons: Overkill for a simple calculation, requires significant effort.
  1. Other Options?:
  • Question: What have I missed? Are there better approaches or tools I should consider?

What do you think? Any experiences or suggestions would be greatly appreciated!