<filename>homeassistant/components/automation/sun.py
"""
Offer sun based automation rules.

For more details about this automation rule, please refer to the documentation
at https://home-assistant.io/components/automation/#sun-trigger
"""
import logging
from datetime import timedelta

import homeassistant.util.dt as dt_util
from homeassistant.components import sun
from homeassistant.helpers.event import track_sunrise, track_sunset

DEPENDENCIES = ['sun']

CONF_OFFSET = 'offset'
CONF_EVENT = 'event'
CONF_BEFORE = "before"
CONF_BEFORE_OFFSET = "before_offset"
CONF_AFTER = "after"
CONF_AFTER_OFFSET = "after_offset"

EVENT_SUNSET = 'sunset'
EVENT_SUNRISE = 'sunrise'

_LOGGER = logging.getLogger(__name__)


def trigger(hass, config, action):
    """Listen for events based on configuration."""
    event = config.get(CONF_EVENT)

    if event is None:
        _LOGGER.error("Missing configuration key %s", CONF_EVENT)
        return False

    event = event.lower()
    if event not in (EVENT_SUNRISE, EVENT_SUNSET):
        _LOGGER.error("Invalid value for %s: %s", CONF_EVENT, event)
        return False

    offset = _parse_offset(config.get(CONF_OFFSET))
    if offset is False:
        return False

    # Do something to call action
    if event == EVENT_SUNRISE:
        track_sunrise(hass, action, offset)
    else:
        track_sunset(hass, action, offset)

    return True


def if_action(hass, config):
    """Wrap action method with sun based condition."""
    before = config.get(CONF_BEFORE)
    after = config.get(CONF_AFTER)

    # Make sure required configuration keys are present
    if before is None and after is None:
        logging.getLogger(__name__).error(
            "Missing if-condition configuration key %s or %s",
            CONF_BEFORE, CONF_AFTER)
        return None

    # Make sure configuration keys have the right value
    if before not in (None, EVENT_SUNRISE, EVENT_SUNSET) or \
       after not in (None, EVENT_SUNRISE, EVENT_SUNSET):
        logging.getLogger(__name__).error(
            "%s and %s can only be set to %s or %s",
            CONF_BEFORE, CONF_AFTER, EVENT_SUNRISE, EVENT_SUNSET)
        return None

    before_offset = _parse_offset(config.get(CONF_BEFORE_OFFSET))
    after_offset = _parse_offset(config.get(CONF_AFTER_OFFSET))
    if before_offset is False or after_offset is False:
        return None

    if before is None:
        def before_func():
            """Return no point in time."""
            return None
    elif before == EVENT_SUNRISE:
        def before_func():
            """Return time before sunrise."""
            return sun.next_rising(hass) + before_offset
    else:
        def before_func():
            """Return time before sunset."""
            return sun.next_setting(hass) + before_offset

    if after is None:
        def after_func():
            """Return no point in time."""
            return None
    elif after == EVENT_SUNRISE:
        def after_func():
            """Return time after sunrise."""
            return sun.next_rising(hass) + after_offset
    else:
        def after_func():
            """Return time after sunset."""
            return sun.next_setting(hass) + after_offset

    def time_if():
        """Validate time based if-condition."""
        now = dt_util.now()
        before = before_func()
        after = after_func()

        if before is not None and now > now.replace(hour=before.hour,
                                                    minute=before.minute):
            return False

        if after is not None and now < now.replace(hour=after.hour,
                                                   minute=after.minute):
            return False

        return True

    return time_if


def _parse_offset(raw_offset):
    """Parse the offset."""
    if raw_offset is None:
        return timedelta(0)

    negative_offset = False
    if raw_offset.startswith('-'):
        negative_offset = True
        raw_offset = raw_offset[1:]

    try:
        (hour, minute, second) = [int(x) for x in raw_offset.split(':')]
    except ValueError:
        _LOGGER.error('Could not parse offset %s', raw_offset)
        return False

    offset = timedelta(hours=hour, minutes=minute, seconds=second)

    if negative_offset:
        offset *= -1

    return offset

<filename>homeassistant/components/automation/sun.py
"""
Offer sun based automation rules.

For more details about this automation rule, please refer to the documentation
at https://home-assistant.io/components/automation/#sun-trigger
"""
import logging
from datetime import timedelta

import homeassistant.util.dt as dt_util
from homeassistant.components import sun
from homeassistant.helpers.event import track_sunrise, track_sunset

DEPENDENCIES = ['sun']

CONF_OFFSET = 'offset'
CONF_EVENT = 'event'
CONF_BEFORE = "before"
CONF_BEFORE_OFFSET = "before_offset"
CONF_AFTER = "after"
CONF_AFTER_OFFSET = "after_offset"

EVENT_SUNSET = 'sunset'
EVENT_SUNRISE = 'sunrise'

_LOGGER = logging.getLogger(__name__)


def trigger(hass, config, action):
    """Listen for events based on configuration."""
    event = config.get(CONF_EVENT)

    if event is None:
        _LOGGER.error("Missing configuration key %s", CONF_EVENT)
        return False

    event = event.lower()
    if event not in (EVENT_SUNRISE, EVENT_SUNSET):
        _LOGGER.error("Invalid value for %s: %s", CONF_EVENT, event)
        return False

    offset = _parse_offset(config.get(CONF_OFFSET))
    if offset is False:
        return False

    # Do something to call action
    if event == EVENT_SUNRISE:
        track_sunrise(hass, action, offset)
    else:
        track_sunset(hass, action, offset)

    return True


def if_action(hass, config):
    """Wrap action method with sun based condition."""
    before = config.get(CONF_BEFORE)
    after = config.get(CONF_AFTER)

    # Make sure required configuration keys are present
    if before is None and after is None:
        logging.getLogger(__name__).error(
            "Missing if-condition configuration key %s or %s",
            CONF_BEFORE, CONF_AFTER)
        return None

    # Make sure configuration keys have the right value
    if before not in (None, EVENT_SUNRISE, EVENT_SUNSET) or \
       after not in (None, EVENT_SUNRISE, EVENT_SUNSET):
        logging.getLogger(__name__).error(
            "%s and %s can only be set to %s or %s",
            CONF_BEFORE, CONF_AFTER, EVENT_SUNRISE, EVENT_SUNSET)
        return None

    before_offset = _parse_offset(config.get(CONF_BEFORE_OFFSET))
    after_offset = _parse_offset(config.get(CONF_AFTER_OFFSET))
    if before_offset is False or after_offset is False:
        return None

    if before is None:
        def before_func():
            """Return no point in time."""
            return None
    elif before == EVENT_SUNRISE:
        def before_func():
            """Return time before sunrise."""
            return sun.next_rising(hass) + before_offset
    else:
        def before_func():
            """Return time before sunset."""
            return sun.next_setting(hass) + before_offset

    if after is None:
        def after_func():
            """Return no point in time."""
            return None
    elif after == EVENT_SUNRISE:
        def after_func():
            """Return time after sunrise."""
            return sun.next_rising(hass) + after_offset
    else:
        def after_func():
            """Return time after sunset."""
            return sun.next_setting(hass) + after_offset

    def time_if():
        """Validate time based if-condition."""
        now = dt_util.now()
        before = before_func()
        after = after_func()

        if before is not None and now > now.replace(hour=before.hour,
                                                    minute=before.minute):
            return False

        if after is not None and now < now.replace(hour=after.hour,
                                                   minute=after.minute):
            return False

        return True

    return time_if


def _parse_offset(raw_offset):
    """Parse the offset."""
    if raw_offset is None:
        return timedelta(0)

    negative_offset = False
    if raw_offset.startswith('-'):
        negative_offset = True
        raw_offset = raw_offset[1:]

    try:
        (hour, minute, second) = [int(x) for x in raw_offset.split(':')]
    except ValueError:
        _LOGGER.error('Could not parse offset %s', raw_offset)
        return False

    offset = timedelta(hours=hour, minutes=minute, seconds=second)

    if negative_offset:
        offset *= -1

    return offset

Offer sun based automation rules.  For more details about this automation rule, please refer to the documentation at https://home-assistant.io/components/automation/#sun-trigger Listen for events based on configuration. # Do something to call action Wrap action method with sun based condition. # Make sure required configuration keys are present # Make sure configuration keys have the right value Return no point in time. Return time before sunrise. Return time before sunset. Return no point in time. Return time after sunrise. Return time after sunset. Validate time based if-condition. Parse the offset.