Templating Help

@petro or any other templating guru out there I would like to be able to do the following.

Calculate an estimate in predicted electricity spend. I was thinking of using a calculation for daily average so far so would need a template to tell me how many days into the current month it is.

I would then want to use that average and times it by the number of remaining days left in the current month.

Template one - how many days into current month.

Template two - how many days left in current month.

Found this:

1 Like

So, here’s what I would use. Works even during leap years. Gotta use the time_date platform to get a sensor that updates once a day. Using this will create sensor.date.

sensor:
  - platform: time_date
    display_options:
      - 'date'
    
  - platform: template
    sensors:
      remaining_days:
        entity_id:
          - sensor.date
        value_template: >
          {% set this = now().replace(hour=0).replace(minute=0).replace(second=0).replace(microsecond=0) %}
          {% set next = this.month + 1 if this.month + 1 <= 12 else 1 %}
          {% set last = this.replace(month=next, day=1) %}
          {{ (last.date() - this.date()).days }}
  - platform: template
    sensors:
      past_days:
        entity_id:
          - sensor.date
        value_template: >
          {% set this = now().replace(hour=0).replace(minute=0).replace(second=0).replace(microsecond=0) %}
          {% set next = this.month + 1 if this.month + 1 <= 12 else 1 %}
          {% set first = this.replace(day=1) %}
          {{ (this.date() - first.date()).days }}
1 Like

lol thats way more elegant then what I came up with

 cost_monthly_left:
    friendly_name: "Month Left"
    value_template: >-
      {% if now().month in [1,3,5,7,8,10,12] %}
        {{ 31 - now().day }}
      {% elif now().month in [4,6,9,11] %}
        {{ 30 - now().day }}
      {% elif now().month == 2 and ((now().year-2000) % 4 > 0) %}
        {{ 28 - now().day }}
      {% elif now().month == 2 and ((now().year-2000) % 4 == 0) %}
        {{ 29 - now().day }}
      {% endif %}

  cost_daily_ave:
    friendly_name: "Daily Ave"
    value_template: "{{ (states.sensor.monthly_total.state|float) / ( now().day) }}"
    
  cost_prediction:
    friendly_name: "Cost Prediction"
    value_template: "{{ ((states.sensor.cost_daily_ave.state|float) * (states.sensor.cost_monthly_left.state|float) + (states.sensor.monthly_total.state|float)) }}"

Yeah, I try to keep my templates short so I don’t scroll for hours in files.

Is it by design that remaining_days includes the current day?

For example, today is the 10th of May, a month with 31 days. The template reports there are 22 days remaining this month. Therefore it includes the current day (the 10th) as a ‘remaining day’.

In other words, if today was the 31st of May it would report there is one remaining day left.

I’m not claiming anything is wrong and only asking if this is the desired definition of ‘remaining days’ for this particular application.


EDIT
I think I’m overlooking the fact this is looking at the remaining days next month … I see what its doing. It uses the first day of the next month , last.date(), to represent the end of the period.

Probably not, i should have subtracted 1 from this and added 1 to the other template. Really depends on what he wants. The calculation requires using the next months day 1 as that’s the only way to dynamically get to the last day of each month.

Clever way of doing it :+1: given that python’s monthrange is unavailable for use here.

1 Like

Yeah, it’s a workaround for sure but it gets the job done!

this is really interesting, having made a related sensor myself, would you please check if could be elegantized…:

        value_template: >
          {% set day_delta = states('sensor.number_of_days_next_alarm_macro') %}
          {% set day = now().day %}
          {% set month = now().month %}
          {% set month_days = states('sensor.days_current_month')|int %}
          {% set month_delta = 1 if month < 12  else 1-12 %}

          {% if is_state('input_boolean.alarmclock_wd_enabled','off') and
                is_state('input_boolean.alarmclock_we_enabled','off') %} {% set daytype = 'Not set' %}
          {% elif day_delta == '0' %} {% set daytype = 'Today,'%}
          {% elif day_delta == '1' %} {% set daytype = 'Tomorrow,' %}
          {% elif day_delta == '2' %} {% set daytype = 'The day after tomorrow,' %}
          {% elif day_delta > '2' %} {% set daytype = 'Next' %}
          {% endif %}

          {% if day_delta == 'Not set' %} Not set
          {% elif day + day_delta|int > month_days %}
             {{daytype}} {{ as_timestamp(now().replace(month= month + month_delta).replace(day=(day + 
                                                day_delta|int - month_days))) | timestamp_custom('%A, %-d %B')}}
          {% else %}
             {{daytype}} {{ as_timestamp(now().replace(day=(day + day_delta|int )))| timestamp_custom('%A, %-d %B') }}
          {% endif %}

of course the challenge lies in the bottom part of the template where the replacement is done.

calculating wether today + number days til next alarm crosses the month days was the other.

my leap years in the current month sensor:

      days_current_month:
        friendly_name: Days current month
        entity_id: sensor.date
        value_template: >
          {% set month = now().strftime('%-m') %}
          {% if month in ['1','3','5','7','8','10','12'] %} 31
          {% elif month in ['4','6','9','11'] %} 30
          {% else %} {{'29' if (now().strftime('%-y'))|int//4 == 0 else '28'}}
          {% endif %}

Slightly refactored:

      days_current_month:
        friendly_name: Days current month
        entity_id: sensor.date
        value_template: >
          {% set month = now().month %}
          {% if month in [1,3,5,7,8,10,12] %} 31
          {% elif month in [4,6,9,11] %} 30
          {% else %} {{29 if (now().year)|int//4 == 0 else 28}}
          {% endif %}

One more refactoring. You can replace this:


{% if is_state('input_boolean.alarmclock_wd_enabled','off') and
      is_state('input_boolean.alarmclock_we_enabled','off') %} {% set daytype = 'Not set' %}
{% elif day_delta == '0' %} {% set daytype = 'Today,'%}
{% elif day_delta == '1' %} {% set daytype = 'Tomorrow,' %}
{% elif day_delta == '2' %} {% set daytype = 'The day after tomorrow,' %}
{% elif day_delta > '2' %} {% set daytype = 'Next' %}
{% endif %}

With this:

{% set daytypes = ['Today,', 'Tomorrow,', 'The day after tomorrow,'] %}
{% set disabled = is_state('input_boolean.alarmclock_wd_enabled','off') and is_state('input_boolean.alarmclock_we_enabled','off') %}
{% set daytype = 'Not set' if disabled else daytypes[day_delta] if day_delta < 3 else 'Next' %}
1 Like

Not really much optimization

      days_current_month:
        friendly_name: Days current month
        entity_id: sensor.date
        value_template: >
          {% set this = now() %}
          {% set next = this.month + 1 if this.month + 1 <= 12 else 1 %}
          {% set first = this.replace(day=1) %}
          {% set last = first .replace(month=next) %}
          {{ (last - first).days }}

Some optimization, some of bit of @123’s code.

          {% set timezone = '+04:00' %}
          {% set delta = states('sensor.number_of_days_next_alarm_macro') %}
          {% if delta == 'Not set' %} {{ delta }}
          {% else %}
            {% set delta = delta | int + 1 %}
            {% set time = now() %}
            {% set mapper = [ 'Today', 'Tomorrow', 'The day after tomorrow,' ] %}
            {% set disabled = is_state('input_boolean.alarmclock_wd_enabled','off') and is_state('input_boolean.alarmclock_we_enabled','off') %}
            {% set daystring = 'Not set' if disabled else mapper[delta] if delta < mapper | length else 'Next' %}
            {% set time = now() %}
            {% set current = time.strftime('%j') | int %}
            {% set next = current + delta  if current + delta <= 365 else (current + delta)-365 %}
            {% set year = time.year if current + delta <= 365 else time.year + 1 %}
            {% set next = '{} {} 00:00:00{}'.format(year, next, timezone) %}
            {% set next = as_timestamp(strptime(next , '%Y %j %H:%M:%S%z')) | timestamp_custom('%A, %-d %B') %}
            {{ daystring }} {{ next }}
          {% endif %}

You gotta add your own timezone instead of '+04:00'

o wow, you must be enjoying this :wink:

I am for one. Thanks!

this gives me Next none however, and I will have to study your template before I can pin down why…
yes, Ive changed the timezone

I edited it a bunch because it wasn’t working over the yearly cross. Make sure you have the latest edit.

really sorry, but it still is not correct:

when set for later today (0 days):

set for next monday (3 days):

set for tomorrow (1 day):

its confusing…

what do you get in the template editor if you do just this

{{ now().strftime('%j') }}

you templating wizards are great to watch in action :slight_smile:

1 Like

For me it reports 130 (i.e. number of days since the beginning of the year).

Screenshot%20from%202019-05-10%2015-15-00

that results in
130