Okay, we’ve seen that ‘astral’ determination (via package) is a bit of a finger in the air.
The best way to do this is via an iterative algorhithm that we just can’t process via Jinja2
So I did the full calcs for the location as specified (well Berlin as in 52.52° North and 13.4° East)
This gave me the exact times of the sun elevation at 24° through the year.
I then got another algorhithm to look at that real data and produce a 6th order polynominal (well 2 actually) by looking at that data to try to reproduce the same results.
The output is based on day of the year so can be repeated year after year (though variations will occur due to both the 4 and 100 year UTC matching) The formulas give correlation factors (R2) of 0.9995 and 0.9996 and comparisons against properly calculated figures give maximum errors of 1 minute 08 seconds at ‘one’ part (but as the sensors given below only have a resolution of 1 minute …
So I can’t say I’d use these myself (it just offends the OCD in me) but they are what they are.
I have given the sensor for the time ‘bracket’ (i.e. time between 24° in the morning and 24° in the afternoon) but you can copy and adjust (with the commented out sections) for the bracket ‘start time’ or the ‘stop time’.
This is incase you want to start an action when the slot starts or when it stops. Use a template trigger : -
"{{ states('sensor.time') == states('sensor.s_berlin_elev24') }}"
But you ‘may’ have to prevent triggers at midnight with a condition when the sun in winter does not reach 24°
The sensor(s) : -
sensor:
- platform: template
sensors:
s_berlin_elev24:
friendly_name: Sun Elevation Bracket
value_template: >
{% set tdy = as_local(states.sensor.date.last_updated).strftime('%j') | int %}
{% set tstdy = as_timestamp(states.sensor.date.last_changed.replace(hour=0,minute=0,second=0,microsecond=0)) %}
{% set strt = (0.00000000252469998477268*(tdy**6) - 0.00000251059967892875*(tdy**5) + 0.000993142676065872*(tdy**4) - 0.200203193424386*(tdy**3) + 22.711821494624*(tdy**2) - 1541.2614147067*tdy + 79570.0417881012) | int %}
{% set stop = (0 - 0.00000000258919433582163*(tdy**6) + 0.00000276855794001385*(tdy**5) - 0.00118433434658982*(tdy**4) + 0.256722704303684*(tdy**3) - 30.2558065308257*(tdy**2) + 1962.38214886188*tdy + 1143.75953197479) | int %}
{% set bracket = stop - strt %}
{% set dytst = 0 if (tdy < 44 or tdy > 301 or strt > 43200 or stop < 43200) else 1 %}
{#{ (tstdy + dytst*strt) | timestamp_custom("%H:%M", false) }#}
{{ (tstdy + dytst*bracket) | timestamp_custom("%H:%M", false) }}
{#{ (tstdy + dytst*stop) | timestamp_custom("%H:%M", false) }#}
icon_template: "{% if is_state('sensor.s_berlin_elev24', '00:00') %}mdi:weather-sunny-off{% else %}mdi:weather-sunny{% endif %}"
This is written for inclusion in a package file so may need adjusting according to your storage preferences.
It is supplied as is (and has been fully tested), it will not be adjusted/corrected for others use. And to show that even an OCD’s best efforts can not substitute for real calculations.
Having said that. given for the ‘day’ to ‘day’ variation (due to Solar Analema) this should be more accurate than just serving up yesterday’s values.
It also does not require any additional ‘helpers’ to store data.
Any ammendments will be made at your own risk (i.e. I won’t help you fix it)
Additional Information : I found that Jinja2 does not seem to like negative mantissa’s so had to subtract a positive one from zero. There ‘may’ be a better way of doing it.
You will also need sensor.date (and sensor.time, if you want time triggers) set up
Edit : Thinking about this further, The Duration of the ‘bracket’ will always be correct but the start and stop times may be related to UTC (ie not DST) . So the ‘tigger time’ formulas may need ‘adjustment’
{{ (tstdy + dytst*strt) | timestamp_custom("%H:%M", false) }}
becomes something like : -
{{ (tstdy + dytst*strt + local_dst_adjustment) | timestamp_custom("%H:%M", false) }}
[ i.e. - determine if DST is applied to set the adjustment, else 0 ]
Or it may be as simple as : -
{{ (tstdy + dytst*strt) | timestamp_custom("%H:%M", true) }}
Suck it and see ...