Na, they run the tests on it for you. And by they, I mean it’s an automated process. Sure, your pr will have a ton of errors and failures first pass but you can make changes until it works.
It’s up above posted a day ago. I’ve tried sec and 'sec as units_of_measurement as well. I get a state for it but no state history.
I also started a new thread History Graph not working for entity with screenshot and my latest attempts…
I can’t get a state history which is why no history graph - so I’m concentrating on that right now.
I made @pnbruckner changes to also get daylength previous and tomorrow. it is here which is slightly different to @pnbruckner
"""
Support for functionality to keep track of the sun.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/sun/
"""
import asyncio
import logging
from datetime import timedelta
from homeassistant.const import CONF_ELEVATION
from homeassistant.core import callback
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.event import (
async_track_point_in_utc_time, async_track_utc_time_change)
from homeassistant.helpers.sun import (
get_astral_location, get_astral_event_next, get_astral_event_date)
from homeassistant.util import dt as dt_util
_LOGGER = logging.getLogger(__name__)
DOMAIN = 'sun'
ENTITY_ID = 'sun.sun'
STATE_ABOVE_HORIZON = 'above_horizon'
STATE_BELOW_HORIZON = 'below_horizon'
STATE_ATTR_AZIMUTH = 'azimuth'
STATE_ATTR_ELEVATION = 'elevation'
STATE_ATTR_NEXT_DAWN = 'next_dawn'
STATE_ATTR_NEXT_DUSK = 'next_dusk'
STATE_ATTR_NEXT_MIDNIGHT = 'next_midnight'
STATE_ATTR_NEXT_NOON = 'next_noon'
STATE_ATTR_NEXT_RISING = 'next_rising'
STATE_ATTR_NEXT_SETTING = 'next_setting'
STATE_ATTR_SUNRISE = 'sunrise'
STATE_ATTR_SUNSET = 'sunset'
STATE_ATTR_DAYLIGHT = 'daylight'
STATE_ATTR_PREV_DAYLIGHT = 'prev_daylight'
STATE_ATTR_NEXT_DAYLIGHT = 'next_daylight'
@asyncio.coroutine
def async_setup(hass, config):
"""Track the state of the sun."""
if config.get(CONF_ELEVATION) is not None:
_LOGGER.warning(
"Elevation is now configured in home assistant core. "
"See https://home-assistant.io/docs/configuration/basic/")
sun = Sun(hass, get_astral_location(hass))
sun.point_in_time_listener(dt_util.utcnow())
return True
class Sun(Entity):
"""Representation of the Sun."""
entity_id = ENTITY_ID
def __init__(self, hass, location):
"""Initialize the sun."""
self.hass = hass
self.location = location
self._state = self.next_rising = self.next_setting = None
self.sunrise = self.sunset = None
self.daylight = self.prev_daylight = self.next_daylight = None
self.next_dawn = self.next_dusk = None
self.next_midnight = self.next_noon = None
self.solar_elevation = self.solar_azimuth = None
async_track_utc_time_change(hass, self.timer_update, second=30)
@property
def name(self):
"""Return the name."""
return "Sun"
@property
def state(self):
"""Return the state of the sun."""
if self.next_rising > self.next_setting:
return STATE_ABOVE_HORIZON
return STATE_BELOW_HORIZON
@property
def state_attributes(self):
"""Return the state attributes of the sun."""
return {
STATE_ATTR_NEXT_DAWN: self.next_dawn.isoformat(),
STATE_ATTR_NEXT_DUSK: self.next_dusk.isoformat(),
STATE_ATTR_NEXT_MIDNIGHT: self.next_midnight.isoformat(),
STATE_ATTR_NEXT_NOON: self.next_noon.isoformat(),
STATE_ATTR_NEXT_RISING: self.next_rising.isoformat(),
STATE_ATTR_NEXT_SETTING: self.next_setting.isoformat(),
STATE_ATTR_SUNRISE: self.sunrise.isoformat(),
STATE_ATTR_SUNSET: self.sunset.isoformat(),
STATE_ATTR_DAYLIGHT: self.daylight.total_seconds(),
STATE_ATTR_PREV_DAYLIGHT: self.prev_daylight.total_seconds(),
STATE_ATTR_NEXT_DAYLIGHT: self.next_daylight.total_seconds(),
STATE_ATTR_ELEVATION: round(self.solar_elevation, 2),
STATE_ATTR_AZIMUTH: round(self.solar_azimuth, 2)
}
@property
def next_change(self):
"""Datetime when the next change to the state is."""
# next_midnight is next solar midnight. So get actual midnight,
# but subtract a second because point_in_time_listener() will add one.
midnight = dt_util.as_utc(dt_util.start_of_local_day(
dt_util.now()+timedelta(1))-timedelta(seconds=1))
return min(self.next_dawn, self.next_dusk, self.next_midnight,
self.next_noon, self.next_rising, self.next_setting, midnight)
@callback
def update_as_of(self, utc_point_in_time):
"""Update the attributes containing solar events."""
self.next_dawn = get_astral_event_next(
self.hass, 'dawn', utc_point_in_time)
self.next_dusk = get_astral_event_next(
self.hass, 'dusk', utc_point_in_time)
self.next_midnight = get_astral_event_next(
self.hass, 'solar_midnight', utc_point_in_time)
self.next_noon = get_astral_event_next(
self.hass, 'solar_noon', utc_point_in_time)
self.next_rising = get_astral_event_next(
self.hass, 'sunrise', utc_point_in_time)
self.next_setting = get_astral_event_next(
self.hass, 'sunset', utc_point_in_time)
self.sunrise = get_astral_event_date(
self.hass, 'sunrise', utc_point_in_time)
self.sunset = get_astral_event_date(
self.hass, 'sunset', utc_point_in_time)
d = get_astral_event_date(
self.hass, 'daylight', utc_point_in_time)
self.daylight = d[1] - d[0]
d = get_astral_event_date(
self.hass, 'daylight', utc_point_in_time-timedelta(days=1))
self.prev_daylight = d[1] - d[0]
d = get_astral_event_date(
self.hass, 'daylight', utc_point_in_time+timedelta(days=1))
self.next_daylight = d[1] - d[0]
@callback
def update_sun_position(self, utc_point_in_time):
"""Calculate the position of the sun."""
self.solar_azimuth = self.location.solar_azimuth(utc_point_in_time)
self.solar_elevation = self.location.solar_elevation(utc_point_in_time)
@callback
def point_in_time_listener(self, now):
"""Run when the state of the sun has changed."""
self.update_sun_position(now)
self.update_as_of(now)
self.async_schedule_update_ha_state()
# Schedule next update at next_change+1 second so sun state has changed
async_track_point_in_utc_time(
self.hass, self.point_in_time_listener,
self.next_change + timedelta(seconds=1))
@callback
def timer_update(self, time):
"""Needed to update solar elevation and azimuth."""
self.update_sun_position(time)
self.async_schedule_update_ha_state()
Do you have recorder or history configured to exclude this entity, directly or indirectly?
nope. zero exclusions.
If you search your HA log do you see state_change events for this sensor? E.g.:
grep -F 'new_state=<state sensor.daylengthchange' home-assistant.log
Have you seen the sensor’s value change from one day to the next (without any HA restarts)?
EDIT: Do you see any entries in the Logbook at the start of the day. I don’t have exactly the same template sensors as you, but I do have a few based on the custom sun component, and I see these in the Logbook:
No. If I look at midnight (which is when it would change) I see only these:
Well, that’s interesting, because you don’t see “Daylight” in my Logbook either (which is another template sensor I have that is the number of seconds, with a unit_of_measurement of sec.) Hmm. I do see it on the History page, though, and I see a changing graph from since I defined it. I’ll try creating a history_graph with it and see what happens…
EDIT: From the History page:
pretty sure the day length ones yesterday, today and tomorrow are behaving. But my templaye sensor takes today-yesterday to get a change in day length and that’s what I want to graph. Problem is you can’t graph the daylight (if you have hours:minutes:seconds) as it just does the hours which is kinda uninteresting.
My sensors:
sensor:
- platform: template
sensors:
nextsunrise:
friendly_name: 'Next Sunrise'
value_template: >
{{ as_timestamp(states.sun.sun.attributes.next_rising) | timestamp_custom(' %I:%M%p') | replace(" 0", "") }}
icon_template: mdi:weather-sunset-up
nextsunset:
friendly_name: 'Next Sunset'
value_template: >
{{ as_timestamp(states.sun.sun.attributes.next_setting) | timestamp_custom(' %I:%M%p') | replace(" 0", "") }}
icon_template: mdi:weather-sunset-down
sunrisetoday:
friendly_name: 'Sunrise'
value_template: >
{{ as_timestamp(states.sun.sun.attributes.sunrise) | timestamp_custom(' %I:%M%p') | replace(" 0", "") }}
icon_template: mdi:weather-sunset-up
sunsettoday:
friendly_name: 'Sunset'
value_template: >
{{ as_timestamp(states.sun.sun.attributes.sunset) | timestamp_custom(' %I:%M%p') | replace(" 0", "") }}
icon_template: mdi:weather-sunset-down
daylightyesterday:
friendly_name: 'Day Length Yesterday'
value_template: >
{{ (states.sun.sun.attributes.prev_daylight) | timestamp_custom(' %H:%M:%S',false) | replace(" 0", "") }}
icon_template: mdi:weather-sunny
daylighttoday:
friendly_name: 'Day Length Today'
value_template: >
{{ (states.sun.sun.attributes.daylight) | timestamp_custom(' %H:%M:%S',false) | replace(" 0", "") }}
icon_template: mdi:weather-sunny
daylighttomorrow:
friendly_name: 'Day Length Tomorrow'
value_template: >
{{ (states.sun.sun.attributes.next_daylight) | timestamp_custom(' %H:%M:%S',false) | replace(" 0", "") }}
icon_template: mdi:weather-sunny
daylengthchange:
friendly_name: 'Change in Day Length'
unit_of_measurement: "sec"
value_template: >
{{ (states.sun.sun.attributes.daylight) - (states.sun.sun.attributes.prev_daylight) }}
icon_template: mdi:weather-sunny
It’s the daylengthchange that is most interesting and I just don’t understand why it’s not appearing in history or the logbook.
It’s changing and the value is correct. I don’t get it.
So I had the daylengthchange set to hidden as I didn’t want the actual sensor to be displayed at all - just wanted the graph. Removed hidden and now it’s showing a history (blank)
Interesting. I’d have to check the code, but (maybe?) it makes sense that history is not recorded for an entity that is hidden. Don’t know. Now that you’ve un-hidden it, it will be interesting to see if you get history (I guess starting at tonight’s midnight. Or, actually, at the next sun elevation change, which should cause sun.sun to have a state change.) FWIW, I use group views, so I define what is displayed, so I don’t have to set things to hidden.
Well, you could have created a template sensor that is daylight converted to floating point hours (with unit_of_measurement set to hr.) That probably would create a reasonable graph, too. Something like {{ (state_attr('sun.sun', 'daylight') / (60*60)) | round(2) }}
.
I use group views as well but use customize out of habit I guess from before I had views defined and I didn’t want random sensors in the header bar. I certainly didn’t expect hiding it to remove the history. That is an unwanted consequence.
Thanks for that… This has been a good learning exercise and I really appreciate your help Phil @pnbruckner
Ha ha ha!!! This is EXACTLY how I feel about it except I have implemented it and still have no idea why!
Except that it is quite cool. But then a lot of stuff I do / want to do in HA has no real actual compelling reason to implement it except to be cool
Of course. I don’t actually NEED anything… some is just for my interest. This is just something I’m interested in watching and seeing how the day length changes as the seasons change and it’s all at a total cost of $0.
HI,
please allow me to jump in here, if I’d need a separate thread tell me.
I’ve been trying to template the offset to the previous (or last if you will) sunset.
My aim is to use that given offset in automations for triggering outside lights and other presence logic.
had this conversation Read sunset offset at a given time? - #11 by tom_l with @tom_l, end ended up with me asking if this could be done with, or added to your CC…
please have a look?
thx!
@pnbruckner
I’m back again with another feature request
Is it possible to calculate the maximium sun elevation for ‘today’? And if it is could this be included in your sensor?
I believe that is possible. It would just be the elevation at solar noon I think. That could be calculated at midnight along with the other values that don’t change throughout the day. I’ll add a new feature request, but I probably won’t get to it for a bit.
EDIT: https://github.com/pnbruckner/homeassistant-config/issues/66
Well, it’s been a while, but I have a beta available that should do this. Let me know how it works for you (assuming you’re still interested! )
It has been a while and to be honest I have no idea now why I asked for this!
I’m more than happy to see test it and see how it works though. It’s the least I can do given all the effort you put into your components (and perhaps my reason for asking will come back to me).
EDIT: Well it seems to work fine. Today it gives an elevation that matches within 0.04 deg with another source on the internet.
Oops, made a mistake. I used the date of the next solar noon when calculating today’s maximum elevation. I was supposed to use today’s solar noon. Fixed in 1.1.0b2.