Sun component: ephem->astral

This commit is contained in:
Paulus Schoutsen 2015-07-16 19:49:54 -07:00
parent c532a28a98
commit 35f0270688
3 changed files with 68 additions and 54 deletions

View file

@ -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. """

View file

@ -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

View file

@ -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)