diff --git a/homeassistant/components/sun.py b/homeassistant/components/sun.py index f9f10840928..5ab4d6f598b 100644 --- a/homeassistant/components/sun.py +++ b/homeassistant/components/sun.py @@ -28,8 +28,10 @@ def is_on(hass, entity_id=None): return hass.states.is_state(entity_id, STATE_ABOVE_HORIZON) -def next_setting(hass): +def next_setting(hass, entity_id=None): """ Returns the datetime object representing the next sun setting. """ + entity_id = entity_id or ENTITY_ID + state = hass.states.get(ENTITY_ID) try: @@ -40,8 +42,10 @@ def next_setting(hass): return None -def next_rising(hass): +def next_rising(hass, entity_id=None): """ Returns the datetime object representing the next sun rising. """ + entity_id = entity_id or ENTITY_ID + state = hass.states.get(ENTITY_ID) try: @@ -73,6 +77,25 @@ def setup(hass, config): latitude = config[ha.DOMAIN][ha.CONF_LATITUDE] longitude = config[ha.DOMAIN][ha.CONF_LONGITUDE] + # Validate latitude and longitude + observer = ephem.Observer() + + errors = [] + + try: + observer.lat = latitude # pylint: disable=assigning-non-slot + except ValueError: + errors.append("invalid value for latitude given: {}".format(latitude)) + + try: + observer.long = longitude # pylint: disable=assigning-non-slot + except ValueError: + errors.append("invalid value for latitude given: {}".format(latitude)) + + if errors: + logger.error("Error setting up: %s", ", ".join(errors)) + return False + def update_sun_state(now): # pylint: disable=unused-argument """ Method to update the current state of the sun and set time of next setting and rising. """ diff --git a/homeassistant/util.py b/homeassistant/util.py index b4c7966baa3..67188926546 100644 --- a/homeassistant/util.py +++ b/homeassistant/util.py @@ -188,6 +188,8 @@ def validate_config(config, items, logger): """ errors_found = False for domain in items.keys(): + config.setdefault(domain, {}) + errors = [item for item in items[domain] if item not in config[domain]] if errors: diff --git a/test/test_component_sun.py b/test/test_component_sun.py new file mode 100644 index 00000000000..d310b59cb8e --- /dev/null +++ b/test/test_component_sun.py @@ -0,0 +1,98 @@ +""" +test.test_component_chromecast +~~~~~~~~~~~ + +Tests Chromecast component. +""" +# pylint: disable=too-many-public-methods,protected-access +import logging +import unittest +import datetime as dt + +import ephem + +import homeassistant as ha +import homeassistant.components as components +import homeassistant.components.sun as sun + + +class TestSun(unittest.TestCase): + """ Test the sun module. """ + + def setUp(self): # pylint: disable=invalid-name + self.hass = ha.HomeAssistant() + + def tearDown(self): # pylint: disable=invalid-name + """ Stop down stuff we started. """ + self.hass._pool.stop() + + def test_is_on(self): + """ Test is_on method. """ + self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON) + self.assertTrue(sun.is_on(self.hass)) + self.hass.states.set(sun.ENTITY_ID, sun.STATE_BELOW_HORIZON) + self.assertFalse(sun.is_on(self.hass)) + + def test_setting_rising(self): + """ Test retrieving sun setting and rising. """ + # Compare it with the real data + self.assertTrue(sun.setup( + self.hass, + {ha.DOMAIN: { + ha.CONF_LATITUDE: '32.87336', + ha.CONF_LONGITUDE: '117.22743' + }})) + + observer = ephem.Observer() + observer.lat = '32.87336' # pylint: disable=assigning-non-slot + observer.long = '117.22743' # pylint: disable=assigning-non-slot + + body_sun = ephem.Sun() # pylint: disable=no-member + next_rising_dt = ephem.localtime(observer.next_rising(body_sun)) + next_setting_dt = ephem.localtime(observer.next_setting(body_sun)) + + # Home Assistant strips out microseconds + # strip it out of the datetime objects + next_rising_dt = next_rising_dt - dt.timedelta( + microseconds=next_rising_dt.microsecond) + next_setting_dt = next_setting_dt - dt.timedelta( + microseconds=next_setting_dt.microsecond) + + self.assertEqual(next_rising_dt, sun.next_rising(self.hass)) + self.assertEqual(next_setting_dt, sun.next_setting(self.hass)) + + # Point it at a state without the proper attributes + self.hass.states.set(sun.ENTITY_ID, sun.STATE_ABOVE_HORIZON) + self.assertIsNone(sun.next_rising(self.hass)) + self.assertIsNone(sun.next_setting(self.hass)) + + # Point it at a non-existing state + self.assertIsNone(sun.next_rising(self.hass, 'non.existing')) + self.assertIsNone(sun.next_setting(self.hass, 'non.existing')) + + def test_setup(self): + """ Test Sun setup with empty and wrong configs. """ + self.assertFalse(sun.setup(self.hass, {})) + self.assertFalse(sun.setup(self.hass, {sun.DOMAIN: {}})) + self.assertFalse(sun.setup( + self.hass, {ha.DOMAIN: {ha.CONF_LATITUDE: '32.87336'}})) + self.assertFalse(sun.setup( + self.hass, {ha.DOMAIN: {ha.CONF_LONGITUDE: '117.22743'}})) + self.assertFalse(sun.setup( + self.hass, {ha.DOMAIN: {ha.CONF_LATITUDE: 'hello'}})) + self.assertFalse(sun.setup( + self.hass, {ha.DOMAIN: {ha.CONF_LONGITUDE: 'how are you'}})) + self.assertFalse(sun.setup( + self.hass, {ha.DOMAIN: { + ha.CONF_LATITUDE: 'wrong', ha.CONF_LONGITUDE: '117.22743' + }})) + self.assertFalse(sun.setup( + self.hass, {ha.DOMAIN: { + ha.CONF_LATITUDE: '32.87336', ha.CONF_LONGITUDE: 'wrong' + }})) + + # Test with correct config + self.assertTrue(sun.setup( + self.hass, {ha.DOMAIN: { + ha.CONF_LATITUDE: '32.87336', ha.CONF_LONGITUDE: '117.22743' + }}))