Sun component: ephem->astral
This commit is contained in:
parent
c532a28a98
commit
35f0270688
3 changed files with 68 additions and 54 deletions
|
@ -22,18 +22,13 @@ The sun event need to have the type 'sun', which service to call, which event
|
|||
import logging
|
||||
from datetime import timedelta
|
||||
|
||||
try:
|
||||
import ephem
|
||||
except ImportError:
|
||||
# Will be fixed during setup
|
||||
ephem = None
|
||||
|
||||
import homeassistant.util as util
|
||||
import homeassistant.util.dt as dt_util
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.components.scheduler import ServiceEventListener
|
||||
|
||||
DEPENDENCIES = []
|
||||
REQUIREMENTS = ['pyephem>=3.7']
|
||||
REQUIREMENTS = ['astral>=0.8.1']
|
||||
DOMAIN = "sun"
|
||||
ENTITY_ID = "sun.sun"
|
||||
|
||||
|
@ -99,24 +94,29 @@ def next_rising_utc(hass, entity_id=None):
|
|||
|
||||
def setup(hass, config):
|
||||
""" Tracks the state of the sun. """
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
global ephem # pylint: disable=invalid-name
|
||||
if ephem is None:
|
||||
import ephem as ephem_
|
||||
ephem = ephem_
|
||||
|
||||
if None in (hass.config.latitude, hass.config.longitude):
|
||||
logger.error("Latitude or longitude not set in Home Assistant config")
|
||||
_LOGGER.error("Latitude or longitude not set in Home Assistant config")
|
||||
return False
|
||||
|
||||
try:
|
||||
sun = Sun(hass, str(hass.config.latitude), str(hass.config.longitude))
|
||||
except ValueError:
|
||||
# Raised when invalid latitude or longitude is given to Observer
|
||||
logger.exception("Invalid value for latitude or longitude")
|
||||
latitude = util.convert(hass.config.latitude, float)
|
||||
longitude = util.convert(hass.config.longitude, float)
|
||||
errors = []
|
||||
|
||||
if latitude is None:
|
||||
errors.append('Latitude needs to be a decimal value')
|
||||
elif -90 > latitude < 90:
|
||||
errors.append('Latitude needs to be -90 .. 90')
|
||||
|
||||
if longitude is None:
|
||||
errors.append('Longitude needs to be a decimal value')
|
||||
elif -180 > longitude < 180:
|
||||
errors.append('Longitude needs to be -180 .. 180')
|
||||
|
||||
if errors:
|
||||
_LOGGER.error('Invalid configuration received: %s', ", ".join(errors))
|
||||
return False
|
||||
|
||||
sun = Sun(hass, latitude, longitude)
|
||||
sun.point_in_time_listener(dt_util.utcnow())
|
||||
|
||||
return True
|
||||
|
@ -128,13 +128,12 @@ class Sun(Entity):
|
|||
entity_id = ENTITY_ID
|
||||
|
||||
def __init__(self, hass, latitude, longitude):
|
||||
self.hass = hass
|
||||
self.observer = ephem.Observer()
|
||||
# pylint: disable=assigning-non-slot
|
||||
self.observer.lat = latitude
|
||||
# pylint: disable=assigning-non-slot
|
||||
self.observer.long = longitude
|
||||
from astral import Astral
|
||||
|
||||
self.hass = hass
|
||||
self.latitude = latitude
|
||||
self.longitude = longitude
|
||||
self.astral = Astral()
|
||||
self._state = self.next_rising = self.next_setting = None
|
||||
|
||||
@property
|
||||
|
@ -167,17 +166,26 @@ class Sun(Entity):
|
|||
|
||||
def update_as_of(self, utc_point_in_time):
|
||||
""" Calculate sun state at a point in UTC time. """
|
||||
sun = ephem.Sun() # pylint: disable=no-member
|
||||
mod = -1
|
||||
while True:
|
||||
next_rising_dt = self.astral.sunrise_utc(
|
||||
utc_point_in_time +
|
||||
timedelta(days=mod), self.latitude, self.longitude)
|
||||
if next_rising_dt > utc_point_in_time:
|
||||
break
|
||||
mod += 1
|
||||
|
||||
# pylint: disable=assigning-non-slot
|
||||
self.observer.date = ephem.date(utc_point_in_time)
|
||||
mod = -1
|
||||
while True:
|
||||
next_setting_dt = (self.astral.sunset_utc(
|
||||
utc_point_in_time +
|
||||
timedelta(days=mod), self.latitude, self.longitude))
|
||||
if next_setting_dt > utc_point_in_time:
|
||||
break
|
||||
mod += 1
|
||||
|
||||
self.next_rising = self.observer.next_rising(
|
||||
sun,
|
||||
start=utc_point_in_time).datetime().replace(tzinfo=dt_util.UTC)
|
||||
self.next_setting = self.observer.next_setting(
|
||||
sun,
|
||||
start=utc_point_in_time).datetime().replace(tzinfo=dt_util.UTC)
|
||||
self.next_rising = next_rising_dt
|
||||
self.next_setting = next_setting_dt
|
||||
|
||||
def point_in_time_listener(self, now):
|
||||
""" Called when the state of the sun has changed. """
|
||||
|
|
|
@ -9,7 +9,7 @@ pytz>=2015.2
|
|||
zeroconf>=0.16.0
|
||||
|
||||
# Sun (sun)
|
||||
pyephem>=3.7
|
||||
astral>=0.8.1
|
||||
|
||||
# Philips Hue library (lights.hue)
|
||||
phue>=0.8
|
||||
|
|
|
@ -8,7 +8,7 @@ Tests Sun component.
|
|||
import unittest
|
||||
from datetime import timedelta
|
||||
|
||||
import ephem
|
||||
from astral import Astral
|
||||
|
||||
import homeassistant as ha
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
@ -34,29 +34,35 @@ class TestSun(unittest.TestCase):
|
|||
|
||||
def test_setting_rising(self):
|
||||
""" Test retrieving sun setting and rising. """
|
||||
latitude = 32.87336
|
||||
longitude = 117.22743
|
||||
|
||||
# Compare it with the real data
|
||||
self.hass.config.latitude = '32.87336'
|
||||
self.hass.config.longitude = '117.22743'
|
||||
self.hass.config.latitude = latitude
|
||||
self.hass.config.longitude = longitude
|
||||
sun.setup(self.hass, None)
|
||||
|
||||
observer = ephem.Observer()
|
||||
observer.lat = '32.87336' # pylint: disable=assigning-non-slot
|
||||
observer.long = '117.22743' # pylint: disable=assigning-non-slot
|
||||
|
||||
astral = Astral()
|
||||
utc_now = dt_util.utcnow()
|
||||
body_sun = ephem.Sun() # pylint: disable=no-member
|
||||
next_rising_dt = observer.next_rising(
|
||||
body_sun, start=utc_now).datetime().replace(tzinfo=dt_util.UTC)
|
||||
next_setting_dt = observer.next_setting(
|
||||
body_sun, start=utc_now).datetime().replace(tzinfo=dt_util.UTC)
|
||||
|
||||
# Home Assistant strips out microseconds
|
||||
# strip it out of the datetime objects
|
||||
next_rising_dt = dt_util.strip_microseconds(next_rising_dt)
|
||||
next_setting_dt = dt_util.strip_microseconds(next_setting_dt)
|
||||
mod = -1
|
||||
while True:
|
||||
next_rising = (astral.sunrise_utc(utc_now +
|
||||
timedelta(days=mod), latitude, longitude))
|
||||
if next_rising > utc_now:
|
||||
break
|
||||
mod += 1
|
||||
|
||||
self.assertEqual(next_rising_dt, sun.next_rising_utc(self.hass))
|
||||
self.assertEqual(next_setting_dt, sun.next_setting_utc(self.hass))
|
||||
mod = -1
|
||||
while True:
|
||||
next_setting = (astral.sunset_utc(utc_now +
|
||||
timedelta(days=mod), latitude, longitude))
|
||||
if next_setting > utc_now:
|
||||
break
|
||||
mod += 1
|
||||
|
||||
self.assertEqual(next_rising, sun.next_rising_utc(self.hass))
|
||||
self.assertEqual(next_setting, sun.next_setting_utc(self.hass))
|
||||
|
||||
# Point it at a state without the proper attributes
|
||||
self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON)
|
||||
|
|
Loading…
Add table
Reference in a new issue