Enhanced Sun component

If you subtract two Python datetimes (which the attributes of the “point in time” sensors are), you get a Python timedelta. The “period of time” sensors basically do this internally. Unfortunately, I can’t leave them as timedeltas, because as I said, that causes problems with the database. So I was forced to convert them to floats. So the attributes of the “period of time” sensors are floats, and I thought hours would be the most reasonable unit of measurement, since I would think most people would want to see these values in hours.

To provide these values in string representations of timedeltas, I’d have to either double the number of attributes, or double the number of sensor types. And I think this type of string is less easy to use (i.e., in automations, template sensors, etc.)

BTW, it “pops out as h:mm:ss” because that’s what happens when you convert a timedelta to a string (which is what is happening in your template sensor.)

Thanks for understanding and glad you have a way to do what you want.

1 Like

@Mariusthvdb

So I think I have this working. It was harder than expected due to round-off errors in the astral library.

What I found for your location is, on 5-21 the Astronomical Daylight was 22.9 hrs and the Astronomical Night was 0.9 hrs. Then on 5-22, when the sun no longer dipped below -18°, the Astronomical Night was, of course, 0.0, and the Astronomical Daylight jumped to 1487.6 hrs (or 62 days)! After that the Astronomical Night stayed at 0.0, and the Astronomical Daylight decreased 24 hrs each day.

This will continue until 7-22 when the sun again dips below -18° and the Astronomical Daylight will be down to 25.6 hrs (1.1 days), and the Astronomical Night comes back at 0.7 hrs. Thereafter it goes back to “normal” where the Astronomical Daylight continues to decrease a little each day and the Astronomical Night increases a little each day.

I still want to test this some more with various locations around the globe to make sure there aren’t anymore corner cases, but hopefully I can release a new version with this behavior sometime soon.

1 Like

great, looking forward to testing it for you.

how do you check all that? where can we enter out lat/lon and see what that gives as for these astronomical settings?

Looks good. I’m also in agreement with @123 on the naming of this component. Either it should replace sun or make it a new component and have it hold the moon stuff too. Astral or something like that.

I wrote a test Python script that I used to determine the algorithm I’d use. At this point it looks like this:

from datetime import date, datetime, time, timedelta

from astral import AstralError, Location

from homeassistant.util import dt as dt_util


def _get_astral_event(location, solar_depression, event, date):
    try:
        location.solar_depression = solar_depression
        return getattr(location, event)(date)
    except AstralError:
        return 'none'


def _sd_angle(solar_depression):
    angle = {'astronomical': -18, 'nautical': -12, 'civil': -6}
    if solar_depression in angle:
        return angle[solar_depression]
    return solar_depression


def _get_period(location, solar_depression, daylight, today):
    if daylight:
        if (location.solar_elevation(location.solar_noon(today))
                < _sd_angle(solar_depression)):
            return 0
        start_event, end_event, end_offset = 'dawn', 'dusk', 0
    else:
        if (location.solar_elevation(location.solar_midnight(today))
                >= _sd_angle(solar_depression)):
            return 0
        start_event, end_event, end_offset = 'dusk', 'dawn', 1
    prev_end = _get_astral_event(location, solar_depression, end_event,
                                 today+timedelta(days=end_offset-1))
    start = _get_astral_event(location, solar_depression, start_event, today)
    end = _get_astral_event(location, solar_depression, end_event,
                            today+timedelta(days=end_offset))
    next_start = _get_astral_event(location, solar_depression, start_event,
                                   today+timedelta(days=1))
    # Due to rounding errors, it's possible for a dawn to be returned when the
    # previous day did not return a dusk, and vice versa. Drop these outliers.
    if prev_end == 'none':
        start = 'none'
    if next_start == 'none':
        end = 'none'
    if start == 'none':
        if daylight:
            start = dt_util.as_local(dt_util.as_utc(datetime.combine(
                today, time())))
        else:
            return 0
    if end == 'none':
        for days in range(1, 366):
            end = _get_astral_event(location, solar_depression, end_event,
                                    today+timedelta(days=days+end_offset))
            if end != 'none':
                break
        if end == 'none':
            return 'none'
    return (end - start).total_seconds()/3600


def test(latitude, longitude, elevation, time_zone):
    print(latitude, longitude, elevation, time_zone)
    location = Location(('', '', latitude, longitude, time_zone, elevation))
    dt_util.set_default_time_zone(dt_util.get_time_zone(location.timezone))

    for month, day in ((5, 21), (5, 22), (5, 23), (5, 24),
                       (7, 20), (7, 21), (7, 22), (7, 23), (7, 24)):
        today = date(2019, month, day)
        print('----------', today, sep='\n')
        for solar_depression in ('astronomical', ):
            print(solar_depression)
            print(_get_astral_event(location, solar_depression, 'dawn', today))
            print(_get_astral_event(location, solar_depression, 'dusk', today))
            daylight = _get_period(location, solar_depression, True, today)
            night = _get_period(location, solar_depression, False, today)
            print(round(daylight, 3), round(daylight/24, 3))
            print(round(night, 3), round(night/24, 3))

If it makes any difference, me too.

not a real dealbreaker, but I feel for @123 's suggestion
for that reason Ive used entity_namespace: astro for now, which does the trick searching for the sensors in the dev-state.

Honestly, I consider this custom integration half-baked at this point. Maybe I should have released it as 0.1.0 instead of 1.0.0. I think it needs a lot of work yet to be fully usable, even possibly becoming the basis for a replacement of the standard sun component. I’m not too worried about some of these minor details just yet. If you find it useful as it is, great. But hopefully it will be much better before too long.

BTW, the algorithm I came up with for determining daylight and night at the various solar depressions still needs work. The astral library is basically lying sometimes (I assume due to round-off errors), so I have to compensate by looking for corner cases and doing something reasonable. I don’t claim that I’ve got that completely working under all circumstances yet.

I’ll also consider adding moon-related sensors, but that’s not a promise. :slight_smile:

What the heck. I can add “_hms” versions of the yesterday, today & tomorrow attributes. I’ll add that to the list. It finally dawned on me what you were saying. I.e., when you open the more info window, it would be nice to see yesterday, today & tomorrow right next to each other in HH:MM:SS format. :slight_smile: The float values are better for any additional calculations one might want to do, but hms is nicer to look at.

1 Like

Hahaha… thanks Phil. Yeah for calculating from the day length (like to call the difference between today and yesterday, decimal is the way to go…) I was somewhat bemused by your answer yesterday because in the old enhanced sun, you did report it as H:mm:ss but I was doing it in a template with the new one anyway so it didn’t bother me. I also meant to say add extra attributes - not add new sensors… Anyway all is well and thanks.

Are you doing a new release with HMS soon and the other algorithm/solar depressions?

Sorry, I don’t understand the question.

Sorry, this has kind of taken back seat to some of the other things I’ve been working on. I can do the hms stuff soon. However, the other things became more difficult than I expected given that the results from the astral package are sometimes not very precise/deterministic.

EDIT: How does this look?

image

The hms attributes are actually strings because, at least in the past, I’ve seen issues with attributes being timedeltas (with the database, etc.)

1 Like

@klogg @DavidFW1960

Released Sun2 1.3.0

Adds xxx_hms attributes to “period of time” sensors, and adds new max_elevation option.

2 Likes

Thanks Phil… knew you’d been busy with the life360…Looks exactly right.

1 Like

Sorry but I missed the topic, the sun card topic and this topic was open in same time :slight_smile:

How does your custom component handle the wonderful thing called Daylight Savings?

I think I read that your component updates the times at midnight but does this take into account when daylight savings changes the time at 2am?

Every year I am plagued with automation not working correctly due to the even times changing by an hour once the sun rises or sets. I am hoping your component helps with that.

Edit:
Just realized I commented on this back and march and never looked into it. DOH.

First, sorry I didn’t answer the first time you asked.

Are you asking about the older custom integration, or the newer custom integration?

I haven’t looked back at the older one, but the newer one should be unaffected by DST. I.e., the datetimes it reports are time zone aware, so as long as the astral package it uses is returning correct values (which it appears to do), then the sensors should be updated with correct values.This includes both the “point in time” type sensors (like sunset), as well as the “period of time” sensors (like daylight.)

Do we still need to put into configuration.yaml the section regarding ‘sun’ ? Or we only need to add this component and the sensor section ?
Can we safely delete the ‘sun’ custom component now that we have ‘sun2’ component?

My sun2 integration does not depend on the standard sun component. And as far as I know, automation sun triggers & conditions do not depend on the standard sun component either. (They all use common sun “helper” code, though.) So, to my knowledge, there’s no reason to include the standard sun component in your configuration if you’re not directly using it.