With DST (Daylight Savings Time) tomorrow I wrote a script to notify me

This is just a contribution that is working for me. It’s based on the example that @123 wrote a while back but I did a bit more so that my house can notify me (via TTS) to change the clocks.

- platform: template
  sensors:        
  
    # Sensor to indicate if we are in DST
    is_dst:
      entity_id: sensor.date
      value_template: "{{ now().timetuple().tm_isdst > 0 }}"  

    # Sensor to indicate if tomorrow the timezone changes (DST vs non DST)  
    dst_change_tomorrow:
      entity_id: sensor.date
      value_template: >
        {% set dt = now() + timedelta(days=1) %}
        {{ now().astimezone().tzinfo != dt.astimezone().tzinfo }}  

Then I just have a schedule each day that checks if the dst_change_tomorrow is true and act upon it.

4 Likes

as an FYI, you no longer need to use the “entity_id:” to cause the sensor to update. now() is a valid means of entity updates and will update every minute.

as a matter of fact, you actually should be getting notifications in your log to that effect.

but thanks for this. I just saw someone else asking for this yesterday and I played around with it trying to figure out a way of doing it and couldn’t get it to work.

I must say that finding information on the different sub-routines/methods/filters for python datetimes is almost impossible without already knowing what you are looking for exists.

2 Likes

This template find’s it all, you can use it for automations, however, realize that the attributes are nested

  - platform: template
    sensors:
      dst:
        friendly_name: Daylight Savings Times
        device_class: timestamp
        value_template: >
          {%- set ns = namespace(previous = 2, spring=none, fall=none) %}
          {%- set today = strptime(states('sensor.date'), '%Y-%m-%d').astimezone().replace(hour=ns.previous) %}
          {%- for i in range(365) %}
          {%- set day = (today + timedelta(days=i)).astimezone() %}
          {%- if ns.previous - day.hour == -1 %}
          {%- set ns.spring = today + timedelta(days=i) | timestamp_local %}
          {%- elif ns.previous - day.hour == 1 %}
          {%- set ns.fall = today + timedelta(days=i) | timestamp_local %}
          {%- endif %}
          {%- set ns.previous = day.hour %}
          {%- endfor %}
          {{ [ns.spring, ns.fall] | min }}
        attribute_templates:
          next: >
            {%- set ns = namespace(previous = 2, spring=none, fall=none) %}
            {%- set today = strptime(states('sensor.date'), '%Y-%m-%d').astimezone().replace(hour=ns.previous) %}
            {%- for i in range(365) %}
            {%- set day = (today + timedelta(days=i)).astimezone() %}
            {%- if ns.previous - day.hour == -1 %}
            {%- set ns.spring = today + timedelta(days=i) %}
            {%- elif ns.previous - day.hour == 1 %}
            {%- set ns.fall = today + timedelta(days=i) %}
            {%- endif %}
            {%- set ns.previous = day.hour %}
            {%- endfor %}
            {%- set next = [ns.spring, ns.fall] | min %}
            {%- set phrase = 'lose an hour' if next == ns.spring else 'gain an hour' %}
            {"spring": "{{ns.spring}}", "fall": "{{ns.fall}}", "event": "{{next}}", "days_to_event":{{(next-today).days}}, "phrase": "{{phrase}}"}

automation

- alias: DST Warning
  trigger:
    - platform: time
      at: '10:00:00'
    - platform: time
      at: '19:00:00'
  condition:
    - condition: template
      value_template: >
        {{ state_attr('sensor.dst', 'next').days_to_event in [7,1] }}
  action:
    - service: notify.homeowners
      data:
        message: >
          {%- set next = state_attr('sensor.dst', 'next') %}
          {%- set plural = 's' if next.days_to_event > 1 else '' %}
          "Daylight savings in {{ next.days_to_event }} day{{plural}}, you will {{ next.phrase }}!"

EDIT 1:

It requires sensor.date to keep the calculations to once a day.

The automation says “Daylight savings in X days, you will gain an hour!” or “Daylight savings in X days, you will lose an hour!”


EDIT2:

It seems people dont have this setup properly. You have to change previous to the hour in which your daylight savings changes. For me, this occurs at 1:59.99 am, so I use 2 for previous. If your time occurs at 2:05 PM or 14:05, then you should use 15 for previous.

...
{%- set ns = namespace(previous = 15, spring=none, fall=none) %} # example with previous set to 15.
...

Keep in mind that you have to update it in 2 spots, the state and next

EDIT 3: I just made a new version that works everywhere in the world without altering. Seen here:

3 Likes

Certainly a more complicated solution but it’s pretty cool.

I like having everything in 1 place and 1 sensor. Reduces the number of entities and keeps the automations simple. Also, I wanted a datetime object to display the date in the UI and use an automation to trigger off it (using any number of days in advance).

seeing this unfortunately :


taking out the device_class solves that:


would be nice if the state would be the date in date format instate of the timestamp?

{{as_timestamp([ns.spring, ns.fall]|min)|timestamp_custom('%A %-d %B %Y')}}

That’s odd, works for me… That’s why I designed it this way.

image

don’t exactly remember where anymore, but had that invalid timestamp before. I think it was with several Astral sensors in the CC by Phil, on astral timings that were dependent on the suns elevation (which happened only on certain locations north of ours)

Anyways, it works like adjusted,
from:


to:

with the humanly readable state.

Will add the timestamp to the attributes, to have it readily available in the system

only gripe is I need to customize the icon :wink:

Ah, I hide my icons :wink:

1 Like

would it be this Templating - Home Assistant

enforce the ISO conversion via isoformat()

see just above this in the yellow box?

at least it makes this out of it now:

rather have the actual date, but this seems ok too.

The new more advanced code is pretty cool, but mine worked a treat with fewer lines and not spending a day debugging :joy::joy::joy::joy::joy::joy:

1 Like

Awesome.
Scratching my head as to how to make that give me answers for a different timezone to my own?
In hope !

Don’t have to change anything

Edit, sorry I just read your message fully. You want a different time zone from the one you’re in. I’ll take a look at it tonorrow

1 Like

@petro, I want to know when daylight savings start and end in a time zone that is different to the HA system.
To explain more…
I am in Australia and have a HA system here and one in the UK. The timezone changes result in a time difference which varies between 9, 10 or 11 hours due to one of us being in summer whilst the other is in winter !

And if you really want (I do…) you can combine @CO_4X4 and @petro’s solutions into one all encompassing sensor (with a bit of cleaned up formatting :wink:):

dst:
  friendly_name: Daylight Savings Times
  device_class: timestamp
  value_template: >
    {%- set ns = namespace(previous = 2, spring=none, fall=none) %}
    {%- set today = strptime(states('sensor.date'), '%Y-%m-%d').astimezone().replace(hour=ns.previous) %}
    {%- for i in range(365) %}
      {%- set day = (today + timedelta(days=i)).astimezone() %}
      {%- if ns.previous - day.hour == -1 %}
        {%- set ns.spring = today + timedelta(days=i) | timestamp_local %}
      {%- elif ns.previous - day.hour == 1 %}
        {%- set ns.fall = today + timedelta(days=i) | timestamp_local %}
      {%- endif %}
      {%- set ns.previous = day.hour %}
    {%- endfor %}
    {{ [ns.spring, ns.fall] | min }}
  attribute_templates:
    is_dst_active: >
      {{ now().timetuple().tm_isdst > 0 }}
    dst_change_tomorrow: >
      {% set dt = now() + timedelta(days=1) %}
      {{ now().astimezone().tzinfo != dt.astimezone().tzinfo }}
    next: >
      {%- set ns = namespace(previous = 2, spring=none, fall=none) %}
      {%- set today = strptime(states('sensor.date'), '%Y-%m-%d').astimezone().replace(hour=ns.previous) %}
      {%- for i in range(365) %}
        {%- set day = (today + timedelta(days=i)).astimezone() %}
        {%- if ns.previous - day.hour == -1 %}
          {%- set ns.spring = today + timedelta(days=i) %}
        {%- elif ns.previous - day.hour == 1 %}
          {%- set ns.fall = today + timedelta(days=i) %}
        {%- endif %}
        {%- set ns.previous = day.hour %}
      {%- endfor %}
      {%- set next = [ns.spring, ns.fall] | min %}
      {%- set phrase = 'lose an hour' if next == ns.spring else 'gain an hour' %}
      {"spring": "{{ns.spring}}", "fall": "{{ns.fall}}", "event": "{{next}}", "days_to_event":{{(next-today).days}}, "phrase": "{{phrase}}"}

That way I can still easily use the booleans in automations if needed.

does the {{ now().timetuple().tm_isdst}} return the true time difference, or merely a 1 or 0 indicating yes /no for being in a dst time

I ask because the link to 123’s original post doesnt say so, and even he is not sure :wink: Id like to add this to the attributes, to indicate the difference to the time we’re in. -1, 0 or 1, not even sure either … :blush:

attribute_templates:
  time difference: >
    {{now().timetuple().tm_isdst}}

or would we need more than that.

btw I’ve never seen timetuple() being mentioned before, this is rather useful info:

Is this a HA jinja extension? should really be documented somewhere…

I’m far from an expert but it looks like it’s the latter (1/0 = yes/no)

two days ago it was 0. now it’s 1.

thanks, in which case, wouldn’t it be more to the point using

{{ now().timetuple().tm_isdst == 0 }}

or

{{ now().timetuple().tm_isdst == 1 }}

for that matter? simulating the binary effect of that value

I don’t know for sure.

either way > 0 works so no reason to change it until I know differently.

yeah, maybe Petro knows, he’ll hop by some time soon :wink: