mirror of
https://github.com/home-assistant/core.git
synced 2025-04-25 09:47:52 +00:00
Merge pull request #222 from balloob/change-dependency-sun
Sun component: ephem->astral
This commit is contained in:
commit
f4562fa352
@ -22,21 +22,18 @@ The sun event need to have the type 'sun', which service to call, which event
|
|||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
try:
|
import homeassistant.util as util
|
||||||
import ephem
|
|
||||||
except ImportError:
|
|
||||||
# Will be fixed during setup
|
|
||||||
ephem = None
|
|
||||||
|
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.components.scheduler import ServiceEventListener
|
from homeassistant.components.scheduler import ServiceEventListener
|
||||||
|
|
||||||
DEPENDENCIES = []
|
DEPENDENCIES = []
|
||||||
REQUIREMENTS = ['pyephem>=3.7']
|
REQUIREMENTS = ['astral>=0.8.1']
|
||||||
DOMAIN = "sun"
|
DOMAIN = "sun"
|
||||||
ENTITY_ID = "sun.sun"
|
ENTITY_ID = "sun.sun"
|
||||||
|
|
||||||
|
CONF_ELEVATION = 'elevation'
|
||||||
|
|
||||||
STATE_ABOVE_HORIZON = "above_horizon"
|
STATE_ABOVE_HORIZON = "above_horizon"
|
||||||
STATE_BELOW_HORIZON = "below_horizon"
|
STATE_BELOW_HORIZON = "below_horizon"
|
||||||
|
|
||||||
@ -99,24 +96,43 @@ def next_rising_utc(hass, entity_id=None):
|
|||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Tracks the state of the sun. """
|
""" 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):
|
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
|
return False
|
||||||
|
|
||||||
try:
|
latitude = util.convert(hass.config.latitude, float)
|
||||||
sun = Sun(hass, str(hass.config.latitude), str(hass.config.longitude))
|
longitude = util.convert(hass.config.longitude, float)
|
||||||
except ValueError:
|
errors = []
|
||||||
# Raised when invalid latitude or longitude is given to Observer
|
|
||||||
logger.exception("Invalid value for latitude or longitude")
|
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
|
return False
|
||||||
|
|
||||||
|
platform_config = config.get(DOMAIN, {})
|
||||||
|
|
||||||
|
elevation = platform_config.get(CONF_ELEVATION)
|
||||||
|
|
||||||
|
from astral import Location, GoogleGeocoder
|
||||||
|
|
||||||
|
location = Location(('', '', latitude, longitude, hass.config.time_zone,
|
||||||
|
elevation or 0))
|
||||||
|
|
||||||
|
if elevation is None:
|
||||||
|
google = GoogleGeocoder()
|
||||||
|
google._get_elevation(location) # pylint: disable=protected-access
|
||||||
|
_LOGGER.info('Retrieved elevation from Google: %s', location.elevation)
|
||||||
|
|
||||||
|
sun = Sun(hass, location)
|
||||||
sun.point_in_time_listener(dt_util.utcnow())
|
sun.point_in_time_listener(dt_util.utcnow())
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@ -127,14 +143,9 @@ class Sun(Entity):
|
|||||||
|
|
||||||
entity_id = ENTITY_ID
|
entity_id = ENTITY_ID
|
||||||
|
|
||||||
def __init__(self, hass, latitude, longitude):
|
def __init__(self, hass, location):
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
self.observer = ephem.Observer()
|
self.location = location
|
||||||
# pylint: disable=assigning-non-slot
|
|
||||||
self.observer.lat = latitude
|
|
||||||
# pylint: disable=assigning-non-slot
|
|
||||||
self.observer.long = longitude
|
|
||||||
|
|
||||||
self._state = self.next_rising = self.next_setting = None
|
self._state = self.next_rising = self.next_setting = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -167,17 +178,24 @@ class Sun(Entity):
|
|||||||
|
|
||||||
def update_as_of(self, utc_point_in_time):
|
def update_as_of(self, utc_point_in_time):
|
||||||
""" Calculate sun state at a point in UTC 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.location.sunrise(
|
||||||
|
utc_point_in_time + timedelta(days=mod), local=False)
|
||||||
|
if next_rising_dt > utc_point_in_time:
|
||||||
|
break
|
||||||
|
mod += 1
|
||||||
|
|
||||||
# pylint: disable=assigning-non-slot
|
mod = -1
|
||||||
self.observer.date = ephem.date(utc_point_in_time)
|
while True:
|
||||||
|
next_setting_dt = (self.location.sunset(
|
||||||
|
utc_point_in_time + timedelta(days=mod), local=False))
|
||||||
|
if next_setting_dt > utc_point_in_time:
|
||||||
|
break
|
||||||
|
mod += 1
|
||||||
|
|
||||||
self.next_rising = self.observer.next_rising(
|
self.next_rising = next_rising_dt
|
||||||
sun,
|
self.next_setting = next_setting_dt
|
||||||
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)
|
|
||||||
|
|
||||||
def point_in_time_listener(self, now):
|
def point_in_time_listener(self, now):
|
||||||
""" Called when the state of the sun has changed. """
|
""" Called when the state of the sun has changed. """
|
||||||
|
@ -9,7 +9,7 @@ pytz>=2015.2
|
|||||||
zeroconf>=0.16.0
|
zeroconf>=0.16.0
|
||||||
|
|
||||||
# Sun (sun)
|
# Sun (sun)
|
||||||
pyephem>=3.7
|
astral>=0.8.1
|
||||||
|
|
||||||
# Philips Hue library (lights.hue)
|
# Philips Hue library (lights.hue)
|
||||||
phue>=0.8
|
phue>=0.8
|
||||||
|
@ -67,7 +67,7 @@ class TestDeviceSunLightTrigger(unittest.TestCase):
|
|||||||
light.DOMAIN: {CONF_PLATFORM: 'test'}
|
light.DOMAIN: {CONF_PLATFORM: 'test'}
|
||||||
})
|
})
|
||||||
|
|
||||||
sun.setup(self.hass, {})
|
sun.setup(self.hass, {sun.DOMAIN: {sun.CONF_ELEVATION: 0}})
|
||||||
|
|
||||||
def tearDown(self): # pylint: disable=invalid-name
|
def tearDown(self): # pylint: disable=invalid-name
|
||||||
""" Stop down stuff we started. """
|
""" Stop down stuff we started. """
|
||||||
|
@ -8,7 +8,7 @@ Tests Sun component.
|
|||||||
import unittest
|
import unittest
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
import ephem
|
from astral import Astral
|
||||||
|
|
||||||
import homeassistant as ha
|
import homeassistant as ha
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
@ -34,29 +34,35 @@ class TestSun(unittest.TestCase):
|
|||||||
|
|
||||||
def test_setting_rising(self):
|
def test_setting_rising(self):
|
||||||
""" Test retrieving sun setting and rising. """
|
""" Test retrieving sun setting and rising. """
|
||||||
|
latitude = 32.87336
|
||||||
|
longitude = 117.22743
|
||||||
|
|
||||||
# Compare it with the real data
|
# Compare it with the real data
|
||||||
self.hass.config.latitude = '32.87336'
|
self.hass.config.latitude = latitude
|
||||||
self.hass.config.longitude = '117.22743'
|
self.hass.config.longitude = longitude
|
||||||
sun.setup(self.hass, None)
|
sun.setup(self.hass, {sun.DOMAIN: {sun.CONF_ELEVATION: 0}})
|
||||||
|
|
||||||
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()
|
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
|
mod = -1
|
||||||
# strip it out of the datetime objects
|
while True:
|
||||||
next_rising_dt = dt_util.strip_microseconds(next_rising_dt)
|
next_rising = (astral.sunrise_utc(utc_now +
|
||||||
next_setting_dt = dt_util.strip_microseconds(next_setting_dt)
|
timedelta(days=mod), latitude, longitude))
|
||||||
|
if next_rising > utc_now:
|
||||||
|
break
|
||||||
|
mod += 1
|
||||||
|
|
||||||
self.assertEqual(next_rising_dt, sun.next_rising_utc(self.hass))
|
mod = -1
|
||||||
self.assertEqual(next_setting_dt, sun.next_setting_utc(self.hass))
|
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
|
# Point it at a state without the proper attributes
|
||||||
self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON)
|
self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON)
|
||||||
@ -71,7 +77,7 @@ class TestSun(unittest.TestCase):
|
|||||||
""" Test if the state changes at next setting/rising. """
|
""" Test if the state changes at next setting/rising. """
|
||||||
self.hass.config.latitude = '32.87336'
|
self.hass.config.latitude = '32.87336'
|
||||||
self.hass.config.longitude = '117.22743'
|
self.hass.config.longitude = '117.22743'
|
||||||
sun.setup(self.hass, None)
|
sun.setup(self.hass, {})
|
||||||
|
|
||||||
if sun.is_on(self.hass):
|
if sun.is_on(self.hass):
|
||||||
test_state = sun.STATE_BELOW_HORIZON
|
test_state = sun.STATE_BELOW_HORIZON
|
||||||
|
Loading…
x
Reference in New Issue
Block a user