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
- 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
- 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.
- 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?
- 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…
- 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.
- 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!