diff --git a/homeassistant/components/sensor/moon.py b/homeassistant/components/sensor/moon.py new file mode 100644 index 00000000000..b2ee4574eda --- /dev/null +++ b/homeassistant/components/sensor/moon.py @@ -0,0 +1,81 @@ +""" +Support for tracking the moon phases. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.moon/ +""" +import asyncio +import logging + +import voluptuous as vol + +from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.const import (CONF_NAME) +import homeassistant.util.dt as dt_util +from homeassistant.helpers.entity import Entity +import homeassistant.helpers.config_validation as cv + +REQUIREMENTS = ['astral==1.3.3'] + +_LOGGER = logging.getLogger(__name__) + +DEFAULT_NAME = 'Moon' + +ICON = 'mdi:brightness-3' + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, +}) + + +@asyncio.coroutine +def async_setup_platform(hass, config, async_add_devices, discovery_info=None): + """Set up the Moon sensor.""" + name = config.get(CONF_NAME) + + yield from async_add_devices([MoonSensor(name)], True) + return True + + +class MoonSensor(Entity): + """Representation of a Moon sensor.""" + + def __init__(self, name): + """Initialize the sensor.""" + self._name = name + self._state = None + + @property + def name(self): + """Return the name of the device.""" + return self._name + + @property + def state(self): + """Return the state of the device.""" + if self._state >= 21: + return 'Last quarter' + elif self._state >= 14: + return 'Full moon' + elif self._state >= 7: + return 'First quarter' + else: + return 'New moon' + + @property + def unit_of_measurement(self): + """Return the unit the value is expressed in.""" + return 'Phase' + + @property + def icon(self): + """Icon to use in the frontend, if any.""" + return ICON + + @asyncio.coroutine + def async_update(self): + """Get the time and updates the states.""" + from astral import Astral + + today = dt_util.as_local(dt_util.utcnow()).date() + self._state = Astral().moon_phase(today) diff --git a/requirements_all.txt b/requirements_all.txt index 957a1c3b75c..f48a900dc91 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -51,6 +51,7 @@ apcaccess==0.0.4 apns2==0.1.1 # homeassistant.components.sun +# homeassistant.components.sensor.moon astral==1.3.3 # homeassistant.components.light.avion diff --git a/tests/components/sensor/test_moon.py b/tests/components/sensor/test_moon.py new file mode 100644 index 00000000000..1125dab1201 --- /dev/null +++ b/tests/components/sensor/test_moon.py @@ -0,0 +1,56 @@ +"""The test for the moon sensor platform.""" +import unittest +from datetime import datetime +from unittest.mock import patch + +import homeassistant.util.dt as dt_util +from homeassistant.bootstrap import setup_component + +from tests.common import get_test_home_assistant + +DAY1 = datetime(2017, 1, 1, 1, tzinfo=dt_util.UTC) +DAY2 = datetime(2017, 1, 18, 1, tzinfo=dt_util.UTC) + + +class TestMoonSensor(unittest.TestCase): + """Test the Moon sensor.""" + + def setup_method(self, method): + """Set up things to be run when tests are started.""" + self.hass = get_test_home_assistant() + + def teardown_method(self, method): + """Stop everything that was started.""" + self.hass.stop() + + @patch('homeassistant.components.sensor.moon.dt_util.utcnow', + return_value=DAY1) + def test_moon_day1(self, mock_request): + """Test the Moon sensor.""" + config = { + 'sensor': { + 'platform': 'moon', + 'name': 'moon_day1', + } + } + + assert setup_component(self.hass, 'sensor', config) + + state = self.hass.states.get('sensor.moon_day1') + self.assertEqual(state.state, 'New moon') + + @patch('homeassistant.components.sensor.moon.dt_util.utcnow', + return_value=DAY2) + def test_moon_day2(self, mock_request): + """Test the Moon sensor.""" + config = { + 'sensor': { + 'platform': 'moon', + 'name': 'moon_day2', + } + } + + assert setup_component(self.hass, 'sensor', config) + + state = self.hass.states.get('sensor.moon_day2') + self.assertEqual(state.state, 'Full moon')