예제 #1
0
    def test_setup_group(self):
        """ Test setup_group method. """
        # Try to setup a group with mixed groupable states
        self.hass.states.set('device_tracker.Paulus', STATE_HOME)
        self.assertTrue(
            group.setup_group(self.hass, 'person_and_light',
                              ['light.Bowl', 'device_tracker.Paulus']))
        self.assertEqual(
            STATE_ON,
            self.hass.states.get(
                group.ENTITY_ID_FORMAT.format('person_and_light')).state)

        # Try to setup a group with a non existing state
        self.assertNotIn('non.existing', self.hass.states.entity_ids())
        self.assertTrue(
            group.setup_group(self.hass, 'light_and_nothing',
                              ['light.Bowl', 'non.existing']))
        self.assertEqual(
            STATE_ON,
            self.hass.states.get(
                group.ENTITY_ID_FORMAT.format('light_and_nothing')).state)

        # Try to setup a group with non groupable states
        self.hass.states.set('cast.living_room', "Plex")
        self.hass.states.set('cast.bedroom', "Netflix")
        self.assertFalse(
            group.setup_group(self.hass, 'chromecasts',
                              ['cast.living_room', 'cast.bedroom']))

        # Try to setup an empty group
        self.assertFalse(group.setup_group(self.hass, 'nothing', []))
    def test_setup_group(self):
        """ Test setup_group method. """
        # Try to setup a group with mixed groupable states
        self.hass.states.set('device_tracker.Paulus', STATE_HOME)
        self.assertTrue(group.setup_group(
            self.hass, 'person_and_light',
            ['light.Bowl', 'device_tracker.Paulus']))
        self.assertEqual(
            STATE_ON,
            self.hass.states.get(
                group.ENTITY_ID_FORMAT.format('person_and_light')).state)

        # Try to setup a group with a non existing state
        self.assertNotIn('non.existing', self.hass.states.entity_ids())
        self.assertTrue(group.setup_group(
            self.hass, 'light_and_nothing',
            ['light.Bowl', 'non.existing']))
        self.assertEqual(
            STATE_ON,
            self.hass.states.get(
                group.ENTITY_ID_FORMAT.format('light_and_nothing')).state)

        # Try to setup a group with non groupable states
        self.hass.states.set('cast.living_room', "Plex")
        self.hass.states.set('cast.bedroom', "Netflix")
        self.assertFalse(
            group.setup_group(
                self.hass, 'chromecasts',
                ['cast.living_room', 'cast.bedroom']))

        # Try to setup an empty group
        self.assertFalse(group.setup_group(self.hass, 'nothing', []))
예제 #3
0
    def __init__(self, hass, device_scanner):
        self.states = hass.states

        self.device_scanner = device_scanner

        self.error_scanning = TIME_SPAN_FOR_ERROR_IN_SCANNING

        self.lock = threading.Lock()

        self.path_known_devices_file = hass.get_config_path(KNOWN_DEVICES_FILE)

        # Dictionary to keep track of known devices and devices we track
        self.known_devices = {}

        # Did we encounter an invalid known devices file
        self.invalid_known_devices_file = False

        self._read_known_devices_file()

        # Wrap it in a func instead of lambda so it can be identified in
        # the bus by its __name__ attribute.
        def update_device_state(time):  # pylint: disable=unused-argument
            """ Triggers update of the device states. """
            self.update_devices()

        hass.track_time_change(update_device_state)

        hass.services.register(DOMAIN, SERVICE_DEVICE_TRACKER_RELOAD,
                               lambda service: self._read_known_devices_file())

        self.update_devices()

        group.setup_group(hass, GROUP_NAME_ALL_DEVICES, self.device_entity_ids,
                          False)
예제 #4
0
    def __init__(self, hass, device_scanner):
        self.states = hass.states

        self.device_scanner = device_scanner

        self.error_scanning = TIME_SPAN_FOR_ERROR_IN_SCANNING

        self.lock = threading.Lock()

        self.path_known_devices_file = hass.get_config_path(KNOWN_DEVICES_FILE)

        # Dictionary to keep track of known devices and devices we track
        self.known_devices = {}

        # Did we encounter an invalid known devices file
        self.invalid_known_devices_file = False

        self._read_known_devices_file()

        # Wrap it in a func instead of lambda so it can be identified in
        # the bus by its __name__ attribute.
        def update_device_state(time):  # pylint: disable=unused-argument
            """ Triggers update of the device states. """
            self.update_devices()

        hass.track_time_change(update_device_state)

        hass.services.register(DOMAIN,
                               SERVICE_DEVICE_TRACKER_RELOAD,
                               lambda service: self._read_known_devices_file())

        self.update_devices()

        group.setup_group(
            hass, GROUP_NAME_ALL_DEVICES, self.device_entity_ids, False)
예제 #5
0
    def test_setup_group_with_mixed_groupable_states(self):
        """ Try to setup a group with mixed groupable states """
        self.hass.states.set('device_tracker.Paulus', STATE_HOME)
        group.setup_group(self.hass, 'person_and_light',
                          ['light.Bowl', 'device_tracker.Paulus'])

        self.assertEqual(
            STATE_ON,
            self.hass.states.get(
                group.ENTITY_ID_FORMAT.format('person_and_light')).state)
예제 #6
0
    def test_setup_group_with_mixed_groupable_states(self):
        """ Try to setup a group with mixed groupable states """
        self.hass.states.set('device_tracker.Paulus', STATE_HOME)
        group.setup_group(
            self.hass, 'person_and_light',
            ['light.Bowl', 'device_tracker.Paulus'])

        self.assertEqual(
            STATE_ON,
            self.hass.states.get(
                group.ENTITY_ID_FORMAT.format('person_and_light')).state)
예제 #7
0
        def reload_known_devices_service(service):
            """ Reload known devices file. """
            group.remove_group(self.hass, GROUP_NAME_ALL_DEVICES)

            self._read_known_devices_file()

            self.update_devices(datetime.now())

            if self.tracked:
                group.setup_group(self.hass, GROUP_NAME_ALL_DEVICES,
                                  self.device_entity_ids, False)
예제 #8
0
        def reload_known_devices_service(service):
            """ Reload known devices file. """
            group.remove_group(self.hass, GROUP_NAME_ALL_DEVICES)

            self._read_known_devices_file()

            self.update_devices(datetime.now())

            if self.tracked:
                group.setup_group(
                    self.hass, GROUP_NAME_ALL_DEVICES,
                    self.device_entity_ids, False)
예제 #9
0
    def setUp(self):  # pylint: disable=invalid-name
        """ Init needed objects. """
        self.hass = ha.HomeAssistant()

        self.hass.states.set('light.Bowl', STATE_ON)
        self.hass.states.set('light.Ceiling', STATE_OFF)
        self.hass.states.set('switch.AC', STATE_OFF)
        group.setup_group(self.hass, 'init_group',
                          ['light.Bowl', 'light.Ceiling'], False)
        group.setup_group(self.hass, 'mixed_group',
                          ['light.Bowl', 'switch.AC'], False)

        self.group_name = group.ENTITY_ID_FORMAT.format('init_group')
        self.mixed_group_name = group.ENTITY_ID_FORMAT.format('mixed_group')
예제 #10
0
    def setUp(self):  # pylint: disable=invalid-name
        """ Init needed objects. """
        self.hass = ha.HomeAssistant()

        self.hass.states.set('light.Bowl', STATE_ON)
        self.hass.states.set('light.Ceiling', STATE_OFF)
        self.hass.states.set('switch.AC', STATE_OFF)
        group.setup_group(self.hass, 'init_group',
                          ['light.Bowl', 'light.Ceiling'], False)
        group.setup_group(self.hass, 'mixed_group',
                          ['light.Bowl', 'switch.AC'], False)

        self.group_name = group.ENTITY_ID_FORMAT.format('init_group')
        self.mixed_group_name = group.ENTITY_ID_FORMAT.format('mixed_group')
예제 #11
0
    def test_setup_and_monitor_group(self):
        """ Test setup_group method. """

        # Test if group setup in our init mode is ok
        self.assertIn(self.group_name, self.hass.states.entity_ids())

        group_state = self.hass.states.get(self.group_name)
        self.assertEqual(STATE_ON, group_state.state)
        self.assertTrue(group_state.attributes[group.ATTR_AUTO])

        # Turn the Bowl off and see if group turns off
        self.hass.states.set('light.Bowl', STATE_OFF)

        self.hass._pool.block_till_done()

        group_state = self.hass.states.get(self.group_name)
        self.assertEqual(STATE_OFF, group_state.state)

        # Turn the Ceiling on and see if group turns on
        self.hass.states.set('light.Ceiling', STATE_ON)

        self.hass._pool.block_till_done()

        group_state = self.hass.states.get(self.group_name)
        self.assertEqual(STATE_ON, group_state.state)

        # Try to setup a group with mixed groupable states
        self.hass.states.set('device_tracker.Paulus', STATE_HOME)
        self.assertFalse(group.setup_group(
            self.hass, 'person_and_light',
            ['light.Bowl', 'device_tracker.Paulus']))

        # Try to setup a group with a non existing state
        self.assertNotIn('non.existing', self.hass.states.entity_ids())
        self.assertFalse(group.setup_group(
            self.hass, 'light_and_nothing',
            ['light.Bowl', 'non.existing']))

        # Try to setup a group with non groupable states
        self.hass.states.set('cast.living_room', "Plex")
        self.hass.states.set('cast.bedroom', "Netflix")
        self.assertFalse(
            group.setup_group(
                self.hass, 'chromecasts',
                ['cast.living_room', 'cast.bedroom']))

        # Try to setup an empty group
        self.assertFalse(group.setup_group(self.hass, 'nothing', []))
예제 #12
0
    def test_setup_group_with_a_non_existing_state(self):
        """ Try to setup a group with a non existing state """
        grp = group.setup_group(
            self.hass, 'light_and_nothing',
            ['light.Bowl', 'non.existing'])

        self.assertEqual(STATE_ON, grp.state)
예제 #13
0
    def test_setup_and_monitor_group(self):
        """ Test setup_group method. """

        # Test if group setup in our init mode is ok
        self.assertIn(self.group_name, self.hass.states.entity_ids)

        group_state = self.hass.states.get(self.group_name)
        self.assertEqual(comps.STATE_ON, group_state.state)
        self.assertTrue(group_state.attributes[group.ATTR_AUTO])

        # Turn the Bowl off and see if group turns off
        self.hass.states.set('light.Bowl', comps.STATE_OFF)

        self.hass._pool.block_till_done()

        group_state = self.hass.states.get(self.group_name)
        self.assertEqual(comps.STATE_OFF, group_state.state)

        # Turn the Ceiling on and see if group turns on
        self.hass.states.set('light.Ceiling', comps.STATE_ON)

        self.hass._pool.block_till_done()

        group_state = self.hass.states.get(self.group_name)
        self.assertEqual(comps.STATE_ON, group_state.state)

        # Try to setup a group with mixed groupable states
        self.hass.states.set('device_tracker.Paulus', comps.STATE_HOME)
        self.assertFalse(
            group.setup_group(self.hass, 'person_and_light',
                              ['light.Bowl', 'device_tracker.Paulus']))

        # Try to setup a group with a non existing state
        self.assertNotIn('non.existing', self.hass.states.entity_ids)
        self.assertFalse(
            group.setup_group(self.hass, 'light_and_nothing',
                              ['light.Bowl', 'non.existing']))

        # Try to setup a group with non groupable states
        self.hass.states.set('cast.living_room', "Plex")
        self.hass.states.set('cast.bedroom', "Netflix")
        self.assertFalse(
            group.setup_group(self.hass, 'chromecasts',
                              ['cast.living_room', 'cast.bedroom']))

        # Try to setup an empty group
        self.assertFalse(group.setup_group(self.hass, 'nothing', []))
예제 #14
0
    def test_setup_group_with_non_groupable_states(self):
        self.hass.states.set('cast.living_room', "Plex")
        self.hass.states.set('cast.bedroom', "Netflix")

        grp = group.setup_group(self.hass, 'chromecasts',
                                ['cast.living_room', 'cast.bedroom'])

        self.assertEqual(STATE_UNKNOWN, grp.state)
예제 #15
0
    def test_setup_group_with_non_groupable_states(self):
        self.hass.states.set('cast.living_room', "Plex")
        self.hass.states.set('cast.bedroom', "Netflix")

        grp = group.setup_group(
            self.hass, 'chromecasts',
            ['cast.living_room', 'cast.bedroom'])

        self.assertEqual(STATE_UNKNOWN, grp.state)
예제 #16
0
 def setup_group(self):
     """ Initializes group for all tracked devices. """
     entity_ids = (dev.entity_id for dev in self.devices.values()
                   if dev.track)
     self.group = group.setup_group(
         self.hass, GROUP_NAME_ALL_DEVICES, entity_ids, False)
예제 #17
0
def setup(hass, config):
    """ Track states and offer events for switches. """
    logger = logging.getLogger(__name__)

    switches = platform_devices_from_config(config, DOMAIN, hass, logger)

    if not switches:
        return False

    # Setup a dict mapping entity IDs to devices
    ent_to_switch = {}

    no_name_count = 1

    for switch in switches:
        name = switch.get_name()

        if name is None:
            name = "Switch #{}".format(no_name_count)
            no_name_count += 1

        entity_id = util.ensure_unique_string(
            ENTITY_ID_FORMAT.format(util.slugify(name)), ent_to_switch.keys())

        switch.entity_id = entity_id
        ent_to_switch[entity_id] = switch

    # pylint: disable=unused-argument
    @util.Throttle(MIN_TIME_BETWEEN_SCANS)
    def update_states(now):
        """ Update states of all switches. """

        logger.info("Updating switch states")

        for switch in switches:
            switch.update_ha_state(hass)

    update_states(None)

    def handle_switch_service(service):
        """ Handles calls to the switch services. """
        devices = [
            ent_to_switch[entity_id]
            for entity_id in extract_entity_ids(hass, service)
            if entity_id in ent_to_switch
        ]

        if not devices:
            devices = switches

        for switch in devices:
            if service.service == SERVICE_TURN_ON:
                switch.turn_on()
            else:
                switch.turn_off()

            switch.update_ha_state(hass)

    # Track all switches in a group
    group.setup_group(hass, GROUP_NAME_ALL_SWITCHES, ent_to_switch.keys(),
                      False)

    # Update state every 30 seconds
    hass.track_time_change(update_states, second=[0, 30])

    hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_switch_service)

    hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_switch_service)

    return True
예제 #18
0
def setup(hass, config):
    """ Exposes light control via statemachine and services. """

    # Load built-in profiles and custom profiles
    profile_paths = [os.path.join(os.path.dirname(__file__),
                                  LIGHT_PROFILES_FILE),
                     hass.get_config_path(LIGHT_PROFILES_FILE)]
    profiles = {}

    for profile_path in profile_paths:

        if os.path.isfile(profile_path):
            with open(profile_path) as inp:
                reader = csv.reader(inp)

                # Skip the header
                next(reader, None)

                try:
                    for profile_id, color_x, color_y, brightness in reader:
                        profiles[profile_id] = (float(color_x), float(color_y),
                                                int(brightness))

                except ValueError:
                    # ValueError if not 4 values per row
                    # ValueError if convert to float/int failed
                    _LOGGER.error(
                        "Error parsing light profiles from %s", profile_path)

                    return False

    lights = platform_devices_from_config(config, DOMAIN, hass, _LOGGER)

    if not lights:
        return False

    ent_to_light = {}

    no_name_count = 1

    for light in lights:
        name = light.get_name()

        if name is None:
            name = "Light #{}".format(no_name_count)
            no_name_count += 1

        entity_id = util.ensure_unique_string(
            ENTITY_ID_FORMAT.format(util.slugify(name)),
            ent_to_light.keys())

        light.entity_id = entity_id
        ent_to_light[entity_id] = light

    # pylint: disable=unused-argument
    def update_lights_state(now):
        """ Update the states of all the lights. """
        for light in lights:
            light.update_ha_state(hass)

    update_lights_state(None)

    # Track all lights in a group
    group.setup_group(
        hass, GROUP_NAME_ALL_LIGHTS, ent_to_light.keys(), False)

    def handle_light_service(service):
        """ Hande a turn light on or off service call. """
        # Get and validate data
        dat = service.data

        # Convert the entity ids to valid light ids
        lights = [ent_to_light[entity_id] for entity_id
                  in extract_entity_ids(hass, service)
                  if entity_id in ent_to_light]

        if not lights:
            lights = list(ent_to_light.values())

        params = {}

        transition = util.convert(dat.get(ATTR_TRANSITION), int)

        if transition is not None:
            params[ATTR_TRANSITION] = transition

        if service.service == SERVICE_TURN_OFF:
            for light in lights:
                # pylint: disable=star-args
                light.turn_off(**params)

        else:
            # Processing extra data for turn light on request

            # We process the profile first so that we get the desired
            # behavior that extra service data attributes overwrite
            # profile values
            profile = profiles.get(dat.get(ATTR_PROFILE))

            if profile:
                *params[ATTR_XY_COLOR], params[ATTR_BRIGHTNESS] = profile

            if ATTR_BRIGHTNESS in dat:
                # We pass in the old value as the default parameter if parsing
                # of the new one goes wrong.
                params[ATTR_BRIGHTNESS] = util.convert(
                    dat.get(ATTR_BRIGHTNESS), int, params.get(ATTR_BRIGHTNESS))

            if ATTR_XY_COLOR in dat:
                try:
                    # xy_color should be a list containing 2 floats
                    xycolor = dat.get(ATTR_XY_COLOR)

                    # Without this check, a xycolor with value '99' would work
                    if not isinstance(xycolor, str):
                        params[ATTR_XY_COLOR] = [float(val) for val in xycolor]

                except (TypeError, ValueError):
                    # TypeError if xy_color is not iterable
                    # ValueError if value could not be converted to float
                    pass

            if ATTR_RGB_COLOR in dat:
                try:
                    # rgb_color should be a list containing 3 ints
                    rgb_color = dat.get(ATTR_RGB_COLOR)

                    if len(rgb_color) == 3:
                        params[ATTR_XY_COLOR] = \
                            util.color_RGB_to_xy(int(rgb_color[0]),
                                                 int(rgb_color[1]),
                                                 int(rgb_color[2]))

                except (TypeError, ValueError):
                    # TypeError if rgb_color is not iterable
                    # ValueError if not all values can be converted to int
                    pass

            for light in lights:
                # pylint: disable=star-args
                light.turn_on(**params)

        for light in lights:
            light.update_ha_state(hass, True)

    # Update light state every 30 seconds
    hass.track_time_change(update_lights_state, second=[0, 30])

    # Listen for light on and light off service calls
    hass.services.register(DOMAIN, SERVICE_TURN_ON,
                           handle_light_service)

    hass.services.register(DOMAIN, SERVICE_TURN_OFF,
                           handle_light_service)

    return True
예제 #19
0
    def test_setup_empty_group(self):
        """ Try to setup an empty group. """
        grp = group.setup_group(self.hass, 'nothing', [])

        self.assertEqual(STATE_UNKNOWN, grp.state)
예제 #20
0
def setup(hass, config):
    """ Track states and offer events for switches. """
    logger = logging.getLogger(__name__)

    switches = platform_devices_from_config(config, DOMAIN, hass, logger)

    if not switches:
        return False

    # Setup a dict mapping entity IDs to devices
    ent_to_switch = {}

    no_name_count = 1

    for switch in switches:
        name = switch.get_name()

        if name is None:
            name = "Switch #{}".format(no_name_count)
            no_name_count += 1

        entity_id = util.ensure_unique_string(
            ENTITY_ID_FORMAT.format(util.slugify(name)),
            ent_to_switch.keys())

        switch.entity_id = entity_id
        ent_to_switch[entity_id] = switch

    # pylint: disable=unused-argument
    @util.Throttle(MIN_TIME_BETWEEN_SCANS)
    def update_states(now):
        """ Update states of all switches. """

        logger.info("Updating switch states")

        for switch in switches:
            switch.update_ha_state(hass)

    update_states(None)

    def handle_switch_service(service):
        """ Handles calls to the switch services. """
        devices = [ent_to_switch[entity_id] for entity_id
                   in extract_entity_ids(hass, service)
                   if entity_id in ent_to_switch]

        if not devices:
            devices = switches

        for switch in devices:
            if service.service == SERVICE_TURN_ON:
                switch.turn_on()
            else:
                switch.turn_off()

            switch.update_ha_state(hass)

    # Track all switches in a group
    group.setup_group(hass, GROUP_NAME_ALL_SWITCHES,
                      ent_to_switch.keys(), False)

    # Update state every 30 seconds
    hass.track_time_change(update_states, second=[0, 30])

    hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_switch_service)

    hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_switch_service)

    return True
예제 #21
0
def setup(hass, config):
    """ Exposes light control via statemachine and services. """

    logger = logging.getLogger(__name__)

    if not util.validate_config(config, {DOMAIN: [ha.CONF_TYPE]}, logger):
        return False

    light_type = config[DOMAIN][ha.CONF_TYPE]

    if light_type == 'hue':
        light_init = HueLightControl

    else:
        logger.error("Found unknown light type: {}".format(light_type))

        return False

    light_control = light_init(hass, config[DOMAIN])

    ent_to_light = {}
    light_to_ent = {}

    def _update_light_state(light_id, light_state):
        """ Update statemachine based on the LightState passed in. """
        name = light_control.get_name(light_id) or "Unknown Light"

        try:
            entity_id = light_to_ent[light_id]
        except KeyError:
            # We have not seen this light before, set it up

            # Create entity id
            logger.info("Found new light {}".format(name))

            entity_id = util.ensure_unique_string(
                ENTITY_ID_FORMAT.format(util.slugify(name)),
                list(ent_to_light.keys()))

            ent_to_light[entity_id] = light_id
            light_to_ent[light_id] = entity_id

        state_attr = {ATTR_FRIENDLY_NAME: name}

        if light_state.on:
            state = STATE_ON

            if light_state.brightness:
                state_attr[ATTR_BRIGHTNESS] = light_state.brightness

            if light_state.color:
                state_attr[ATTR_XY_COLOR] = light_state.color

        else:
            state = STATE_OFF

        hass.states.set(entity_id, state, state_attr)

    def update_light_state(light_id):
        """ Update the state of specified light. """
        _update_light_state(light_id, light_control.get(light_id))

    # pylint: disable=unused-argument
    def update_lights_state(time, force_reload=False):
        """ Update the state of all the lights. """

        # First time this method gets called, force_reload should be True
        if force_reload or \
           datetime.now() - update_lights_state.last_updated > \
           MIN_TIME_BETWEEN_SCANS:

            logger.info("Updating light status")
            update_lights_state.last_updated = datetime.now()

            for light_id, light_state in light_control.gets().items():
                _update_light_state(light_id, light_state)

    # Update light state and discover lights for tracking the group
    update_lights_state(None, True)

    if len(ent_to_light) == 0:
        logger.error("No lights found")
        return False

    # Track all lights in a group
    group.setup_group(
        hass, GROUP_NAME_ALL_LIGHTS, light_to_ent.values(), False)

    # Load built-in profiles and custom profiles
    profile_paths = [os.path.join(os.path.dirname(__file__),
                                  LIGHT_PROFILES_FILE),
                     hass.get_config_path(LIGHT_PROFILES_FILE)]
    profiles = {}

    for profile_path in profile_paths:

        if os.path.isfile(profile_path):
            with open(profile_path) as inp:
                reader = csv.reader(inp)

                # Skip the header
                next(reader, None)

                try:
                    for profile_id, color_x, color_y, brightness in reader:
                        profiles[profile_id] = (float(color_x), float(color_y),
                                                int(brightness))

                except ValueError:
                    # ValueError if not 4 values per row
                    # ValueError if convert to float/int failed
                    logger.error(
                        "Error parsing light profiles from {}".format(
                            profile_path))

                    return False

    def handle_light_service(service):
        """ Hande a turn light on or off service call. """
        # Get and validate data
        dat = service.data

        # Convert the entity ids to valid light ids
        light_ids = [ent_to_light[entity_id] for entity_id
                     in extract_entity_ids(hass, service)
                     if entity_id in ent_to_light]

        if not light_ids:
            light_ids = list(ent_to_light.values())

        transition = util.convert(dat.get(ATTR_TRANSITION), int)

        if service.service == SERVICE_TURN_OFF:
            light_control.turn_light_off(light_ids, transition)

        else:
            # Processing extra data for turn light on request

            # We process the profile first so that we get the desired
            # behavior that extra service data attributes overwrite
            # profile values
            profile = profiles.get(dat.get(ATTR_PROFILE))

            if profile:
                *color, bright = profile
            else:
                color, bright = None, None

            if ATTR_BRIGHTNESS in dat:
                bright = util.convert(dat.get(ATTR_BRIGHTNESS), int)

            if ATTR_XY_COLOR in dat:
                try:
                    # xy_color should be a list containing 2 floats
                    xy_color = dat.get(ATTR_XY_COLOR)

                    if len(xy_color) == 2:
                        color = [float(val) for val in xy_color]

                except (TypeError, ValueError):
                    # TypeError if xy_color is not iterable
                    # ValueError if value could not be converted to float
                    pass

            if ATTR_RGB_COLOR in dat:
                try:
                    # rgb_color should be a list containing 3 ints
                    rgb_color = dat.get(ATTR_RGB_COLOR)

                    if len(rgb_color) == 3:
                        color = util.color_RGB_to_xy(int(rgb_color[0]),
                                                     int(rgb_color[1]),
                                                     int(rgb_color[2]))

                except (TypeError, ValueError):
                    # TypeError if rgb_color is not iterable
                    # ValueError if not all values can be converted to int
                    pass

            light_control.turn_light_on(light_ids, transition, bright, color)

        # Update state of lights touched. If there was only 1 light selected
        # then just update that light else update all
        if len(light_ids) == 1:
            update_light_state(light_ids[0])
        else:
            update_lights_state(None, True)

    # Update light state every 30 seconds
    hass.track_time_change(update_lights_state, second=[0, 30])

    # Listen for light on and light off service calls
    hass.services.register(DOMAIN, SERVICE_TURN_ON,
                           handle_light_service)

    hass.services.register(DOMAIN, SERVICE_TURN_OFF,
                           handle_light_service)

    return True
예제 #22
0
def setup(hass, config):
    """ Exposes light control via statemachine and services. """

    if not util.validate_config(config, {DOMAIN: [ha.CONF_TYPE]}, _LOGGER):
        return False

    light_type = config[DOMAIN][ha.CONF_TYPE]

    if light_type == 'hue':
        light_init = get_hue_lights

    else:
        _LOGGER.error("Unknown light type specified: %s", light_type)

        return False

    lights = light_init(hass, config[DOMAIN])

    if len(lights) == 0:
        _LOGGER.error("No lights found")
        return False

    ent_to_light = {}

    no_name_count = 1

    for light in lights:
        name = light.get_name()

        if name is None:
            name = "Light #{}".format(no_name_count)
            no_name_count += 1

        entity_id = util.ensure_unique_string(
            ENTITY_ID_FORMAT.format(util.slugify(name)),
            list(ent_to_light.keys()))

        light.entity_id = entity_id
        ent_to_light[entity_id] = light

    # Load built-in profiles and custom profiles
    profile_paths = [os.path.join(os.path.dirname(__file__),
                                  LIGHT_PROFILES_FILE),
                     hass.get_config_path(LIGHT_PROFILES_FILE)]
    profiles = {}

    for profile_path in profile_paths:

        if os.path.isfile(profile_path):
            with open(profile_path) as inp:
                reader = csv.reader(inp)

                # Skip the header
                next(reader, None)

                try:
                    for profile_id, color_x, color_y, brightness in reader:
                        profiles[profile_id] = (float(color_x), float(color_y),
                                                int(brightness))

                except ValueError:
                    # ValueError if not 4 values per row
                    # ValueError if convert to float/int failed
                    _LOGGER.error(
                        "Error parsing light profiles from %s", profile_path)

                    return False

    # pylint: disable=unused-argument
    def update_lights_state(now):
        """ Update the states of all the lights. """
        for light in lights:
            light.update_ha_state(hass)

    update_lights_state(None)

    # Track all lights in a group
    group.setup_group(
        hass, GROUP_NAME_ALL_LIGHTS, ent_to_light.keys(), False)

    def handle_light_service(service):
        """ Hande a turn light on or off service call. """
        # Get and validate data
        dat = service.data

        # Convert the entity ids to valid light ids
        lights = [ent_to_light[entity_id] for entity_id
                  in extract_entity_ids(hass, service)
                  if entity_id in ent_to_light]

        if not lights:
            lights = list(ent_to_light.values())

        transition = util.convert(dat.get(ATTR_TRANSITION), int)

        if service.service == SERVICE_TURN_OFF:
            for light in lights:
                light.turn_off(transition=transition)

        else:
            # Processing extra data for turn light on request

            # We process the profile first so that we get the desired
            # behavior that extra service data attributes overwrite
            # profile values
            profile = profiles.get(dat.get(ATTR_PROFILE))

            if profile:
                *color, bright = profile
            else:
                color, bright = None, None

            if ATTR_BRIGHTNESS in dat:
                bright = util.convert(dat.get(ATTR_BRIGHTNESS), int)

            if ATTR_XY_COLOR in dat:
                try:
                    # xy_color should be a list containing 2 floats
                    xy_color = dat.get(ATTR_XY_COLOR)

                    if len(xy_color) == 2:
                        color = [float(val) for val in xy_color]

                except (TypeError, ValueError):
                    # TypeError if xy_color is not iterable
                    # ValueError if value could not be converted to float
                    pass

            if ATTR_RGB_COLOR in dat:
                try:
                    # rgb_color should be a list containing 3 ints
                    rgb_color = dat.get(ATTR_RGB_COLOR)

                    if len(rgb_color) == 3:
                        color = util.color_RGB_to_xy(int(rgb_color[0]),
                                                     int(rgb_color[1]),
                                                     int(rgb_color[2]))

                except (TypeError, ValueError):
                    # TypeError if rgb_color is not iterable
                    # ValueError if not all values can be converted to int
                    pass

            for light in lights:
                light.turn_on(transition=transition, brightness=bright,
                              xy_color=color)

        for light in lights:
            light.update_ha_state(hass, True)

    # Update light state every 30 seconds
    hass.track_time_change(update_lights_state, second=[0, 30])

    # Listen for light on and light off service calls
    hass.services.register(DOMAIN, SERVICE_TURN_ON,
                           handle_light_service)

    hass.services.register(DOMAIN, SERVICE_TURN_OFF,
                           handle_light_service)

    return True
예제 #23
0
def setup(hass, config):
    """ Exposes light control via statemachine and services. """

    if not util.validate_config(config, {DOMAIN: [ha.CONF_TYPE]}, _LOGGER):
        return False

    light_type = config[DOMAIN][ha.CONF_TYPE]

    light_init = get_component('light.{}'.format(light_type))

    if light_init is None:
        _LOGGER.error("Unknown light type specified: %s", light_type)

        return False

    lights = light_init.get_lights(hass, config[DOMAIN])

    if len(lights) == 0:
        _LOGGER.error("No lights found")
        return False

    ent_to_light = {}

    no_name_count = 1

    for light in lights:
        name = light.get_name()

        if name is None:
            name = "Light #{}".format(no_name_count)
            no_name_count += 1

        entity_id = util.ensure_unique_string(
            ENTITY_ID_FORMAT.format(util.slugify(name)),
            list(ent_to_light.keys()))

        light.entity_id = entity_id
        ent_to_light[entity_id] = light

    # Load built-in profiles and custom profiles
    profile_paths = [
        os.path.join(os.path.dirname(__file__), LIGHT_PROFILES_FILE),
        hass.get_config_path(LIGHT_PROFILES_FILE)
    ]
    profiles = {}

    for profile_path in profile_paths:

        if os.path.isfile(profile_path):
            with open(profile_path) as inp:
                reader = csv.reader(inp)

                # Skip the header
                next(reader, None)

                try:
                    for profile_id, color_x, color_y, brightness in reader:
                        profiles[profile_id] = (float(color_x), float(color_y),
                                                int(brightness))

                except ValueError:
                    # ValueError if not 4 values per row
                    # ValueError if convert to float/int failed
                    _LOGGER.error("Error parsing light profiles from %s",
                                  profile_path)

                    return False

    # pylint: disable=unused-argument
    def update_lights_state(now):
        """ Update the states of all the lights. """
        for light in lights:
            light.update_ha_state(hass)

    update_lights_state(None)

    # Track all lights in a group
    group.setup_group(hass, GROUP_NAME_ALL_LIGHTS, ent_to_light.keys(), False)

    def handle_light_service(service):
        """ Hande a turn light on or off service call. """
        # Get and validate data
        dat = service.data

        # Convert the entity ids to valid light ids
        lights = [
            ent_to_light[entity_id]
            for entity_id in extract_entity_ids(hass, service)
            if entity_id in ent_to_light
        ]

        if not lights:
            lights = list(ent_to_light.values())

        transition = util.convert(dat.get(ATTR_TRANSITION), int)

        if service.service == SERVICE_TURN_OFF:
            for light in lights:
                light.turn_off(transition=transition)

        else:
            # Processing extra data for turn light on request

            # We process the profile first so that we get the desired
            # behavior that extra service data attributes overwrite
            # profile values
            profile = profiles.get(dat.get(ATTR_PROFILE))

            if profile:
                *color, bright = profile
            else:
                color, bright = None, None

            if ATTR_BRIGHTNESS in dat:
                bright = util.convert(dat.get(ATTR_BRIGHTNESS), int)

            if ATTR_XY_COLOR in dat:
                try:
                    # xy_color should be a list containing 2 floats
                    xy_color = dat.get(ATTR_XY_COLOR)

                    if len(xy_color) == 2:
                        color = [float(val) for val in xy_color]

                except (TypeError, ValueError):
                    # TypeError if xy_color is not iterable
                    # ValueError if value could not be converted to float
                    pass

            if ATTR_RGB_COLOR in dat:
                try:
                    # rgb_color should be a list containing 3 ints
                    rgb_color = dat.get(ATTR_RGB_COLOR)

                    if len(rgb_color) == 3:
                        color = util.color_RGB_to_xy(int(rgb_color[0]),
                                                     int(rgb_color[1]),
                                                     int(rgb_color[2]))

                except (TypeError, ValueError):
                    # TypeError if rgb_color is not iterable
                    # ValueError if not all values can be converted to int
                    pass

            for light in lights:
                light.turn_on(transition=transition,
                              brightness=bright,
                              xy_color=color)

        for light in lights:
            light.update_ha_state(hass, True)

    # Update light state every 30 seconds
    hass.track_time_change(update_lights_state, second=[0, 30])

    # Listen for light on and light off service calls
    hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_light_service)

    hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_light_service)

    return True
예제 #24
0
def setup(hass, config):
    """ Track states and offer events for switches. """
    logger = logging.getLogger(__name__)

    if not util.validate_config(config, {DOMAIN: [ha.CONF_TYPE]}, logger):
        return False

    switch_type = config[DOMAIN][ha.CONF_TYPE]

    switch_init = get_component('switch.{}'.format(switch_type))

    if switch_init is None:
        logger.error("Error loading switch component %s", switch_type)

        return False

    switches = switch_init.get_switches(hass, config[DOMAIN])

    if len(switches) == 0:
        logger.error("No switches found")
        return False

    # Setup a dict mapping entity IDs to devices
    ent_to_switch = {}

    no_name_count = 1

    for switch in switches:
        name = switch.get_name()

        if name is None:
            name = "Switch #{}".format(no_name_count)
            no_name_count += 1

        entity_id = util.ensure_unique_string(
            ENTITY_ID_FORMAT.format(util.slugify(name)),
            list(ent_to_switch.keys()))

        switch.entity_id = entity_id
        ent_to_switch[entity_id] = switch

    # pylint: disable=unused-argument
    def update_states(time, force_reload=False):
        """ Update states of all switches. """

        # First time this method gets called, force_reload should be True
        if force_reload or \
           datetime.now() - update_states.last_updated > \
           MIN_TIME_BETWEEN_SCANS:

            logger.info("Updating switch states")
            update_states.last_updated = datetime.now()

            for switch in switches:
                switch.update_ha_state(hass)

    update_states(None, True)

    def handle_switch_service(service):
        """ Handles calls to the switch services. """
        devices = [
            ent_to_switch[entity_id]
            for entity_id in extract_entity_ids(hass, service)
            if entity_id in ent_to_switch
        ]

        if not devices:
            devices = switches

        for switch in devices:
            if service.service == SERVICE_TURN_ON:
                switch.turn_on()
            else:
                switch.turn_off()

            switch.update_ha_state(hass)

    # Track all wemos in a group
    group.setup_group(hass, GROUP_NAME_ALL_SWITCHES, ent_to_switch.keys(),
                      False)

    # Update state every 30 seconds
    hass.track_time_change(update_states, second=[0, 30])

    hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_switch_service)

    hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_switch_service)

    return True
예제 #25
0
def setup(hass, config):
    """ Exposes light control via statemachine and services. """

    # Load built-in profiles and custom profiles
    profile_paths = [os.path.join(os.path.dirname(__file__),
                                  LIGHT_PROFILES_FILE),
                     hass.get_config_path(LIGHT_PROFILES_FILE)]
    profiles = {}

    for profile_path in profile_paths:

        if os.path.isfile(profile_path):
            with open(profile_path) as inp:
                reader = csv.reader(inp)

                # Skip the header
                next(reader, None)

                try:
                    for profile_id, color_x, color_y, brightness in reader:
                        profiles[profile_id] = (float(color_x), float(color_y),
                                                int(brightness))

                except ValueError:
                    # ValueError if not 4 values per row
                    # ValueError if convert to float/int failed
                    _LOGGER.error(
                        "Error parsing light profiles from %s", profile_path)

                    return False

    lights = platform_devices_from_config(config, DOMAIN, hass, _LOGGER)

    if not lights:
        return False

    ent_to_light = {}

    no_name_count = 1

    for light in lights:
        name = light.get_name()

        if name is None:
            name = "Light #{}".format(no_name_count)
            no_name_count += 1

        entity_id = util.ensure_unique_string(
            ENTITY_ID_FORMAT.format(util.slugify(name)),
            ent_to_light.keys())

        light.entity_id = entity_id
        ent_to_light[entity_id] = light

    # pylint: disable=unused-argument
    def update_lights_state(now):
        """ Update the states of all the lights. """
        for light in lights:
            light.update_ha_state(hass)

    update_lights_state(None)

    # Track all lights in a group
    group.setup_group(
        hass, GROUP_NAME_ALL_LIGHTS, ent_to_light.keys(), False)

    def handle_light_service(service):
        """ Hande a turn light on or off service call. """
        # Get and validate data
        dat = service.data

        # Convert the entity ids to valid light ids
        lights = [ent_to_light[entity_id] for entity_id
                  in extract_entity_ids(hass, service)
                  if entity_id in ent_to_light]

        if not lights:
            lights = list(ent_to_light.values())

        params = {}

        transition = util.convert(dat.get(ATTR_TRANSITION), int)

        if transition is not None:
            params[ATTR_TRANSITION] = transition

        if service.service == SERVICE_TURN_OFF:
            for light in lights:
                # pylint: disable=star-args
                light.turn_off(**params)

        else:
            # Processing extra data for turn light on request

            # We process the profile first so that we get the desired
            # behavior that extra service data attributes overwrite
            # profile values
            profile = profiles.get(dat.get(ATTR_PROFILE))

            if profile:
                *params[ATTR_XY_COLOR], params[ATTR_BRIGHTNESS] = profile

            if ATTR_BRIGHTNESS in dat:
                # We pass in the old value as the default parameter if parsing
                # of the new one goes wrong.
                params[ATTR_BRIGHTNESS] = util.convert(
                    dat.get(ATTR_BRIGHTNESS), int, params.get(ATTR_BRIGHTNESS))

            if ATTR_XY_COLOR in dat:
                try:
                    # xy_color should be a list containing 2 floats
                    xycolor = dat.get(ATTR_XY_COLOR)

                    # Without this check, a xycolor with value '99' would work
                    if not isinstance(xycolor, str):
                        params[ATTR_XY_COLOR] = [float(val) for val in xycolor]

                except (TypeError, ValueError):
                    # TypeError if xy_color is not iterable
                    # ValueError if value could not be converted to float
                    pass

            if ATTR_RGB_COLOR in dat:
                try:
                    # rgb_color should be a list containing 3 ints
                    rgb_color = dat.get(ATTR_RGB_COLOR)

                    if len(rgb_color) == 3:
                        params[ATTR_XY_COLOR] = \
                            util.color_RGB_to_xy(int(rgb_color[0]),
                                                 int(rgb_color[1]),
                                                 int(rgb_color[2]))

                except (TypeError, ValueError):
                    # TypeError if rgb_color is not iterable
                    # ValueError if not all values can be converted to int
                    pass

            if ATTR_FLASH in dat:
                if dat[ATTR_FLASH] == FLASH_SHORT:
                    params[ATTR_FLASH] = FLASH_SHORT

                elif dat[ATTR_FLASH] == FLASH_LONG:
                    params[ATTR_FLASH] = FLASH_LONG

            for light in lights:
                # pylint: disable=star-args
                light.turn_on(**params)

        for light in lights:
            light.update_ha_state(hass, True)

    # Update light state every 30 seconds
    hass.track_time_change(update_lights_state, second=[0, 30])

    # Listen for light on and light off service calls
    hass.services.register(DOMAIN, SERVICE_TURN_ON,
                           handle_light_service)

    hass.services.register(DOMAIN, SERVICE_TURN_OFF,
                           handle_light_service)

    return True
예제 #26
0
def setup(hass, config):
    """ Track states and offer events for switches. """
    logger = logging.getLogger(__name__)

    if not util.validate_config(config, {DOMAIN: [ha.CONF_TYPE]}, logger):
        return False

    switch_type = config[DOMAIN][ha.CONF_TYPE]

    if switch_type == 'wemo':
        switch_init = get_wemo_switches

    else:
        logger.error("Unknown switch type specified: %s", switch_type)

        return False

    switches = switch_init(config[DOMAIN])

    if len(switches) == 0:
        logger.error("No switches found")
        return False

    # Setup a dict mapping entity IDs to devices
    ent_to_switch = {}

    no_name_count = 1

    for switch in switches:
        name = switch.get_name()

        if name is None:
            name = "Switch #{}".format(no_name_count)
            no_name_count += 1

        entity_id = util.ensure_unique_string(
            ENTITY_ID_FORMAT.format(util.slugify(name)),
            list(ent_to_switch.keys()))

        switch.entity_id = entity_id
        ent_to_switch[entity_id] = switch

    # pylint: disable=unused-argument
    def update_states(time, force_reload=False):
        """ Update states of all switches. """

        # First time this method gets called, force_reload should be True
        if force_reload or \
           datetime.now() - update_states.last_updated > \
           MIN_TIME_BETWEEN_SCANS:

            logger.info("Updating switch states")
            update_states.last_updated = datetime.now()

            for switch in switches:
                switch.update_ha_state(hass)

    update_states(None, True)

    def handle_switch_service(service):
        """ Handles calls to the switch services. """
        devices = [ent_to_switch[entity_id] for entity_id
                   in extract_entity_ids(hass, service)
                   if entity_id in ent_to_switch]

        if not devices:
            devices = switches

        for switch in devices:
            if service.service == SERVICE_TURN_ON:
                switch.turn_on()
            else:
                switch.turn_off()

            switch.update_ha_state(hass)

    # Track all wemos in a group
    group.setup_group(hass, GROUP_NAME_ALL_SWITCHES,
                      ent_to_switch.keys(), False)

    # Update state every 30 seconds
    hass.track_time_change(update_states, second=[0, 30])

    hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_switch_service)

    hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_switch_service)

    return True
예제 #27
0
def setup(hass, config):
    """ Track states and offer events for WeMo switches. """
    logger = logging.getLogger(__name__)

    try:
        import homeassistant.external.pywemo.pywemo as pywemo
    except ImportError:
        logger.exception((
            "Failed to import pywemo. "
            "Did you maybe not run `git submodule init` "
            "and `git submodule update`?"))

        return False

    if ha.CONF_HOSTS in config[DOMAIN]:
        devices = []

        for host in config[DOMAIN][ha.CONF_HOSTS].split(","):
            device = pywemo.device_from_host(host)

            if device:
                devices.append(device)

    else:
        logger.info("Scanning for WeMo devices")
        devices = pywemo.discover_devices()

    is_switch = lambda switch: isinstance(switch, pywemo.Switch)

    switches = [device for device in devices if is_switch(device)]

    if len(switches) == 0:
        logger.error("No WeMo switches found")
        return False

    # Dict mapping serial no to entity IDs
    sno_to_ent = {}
    # Dict mapping entity IDs to devices
    ent_to_dev = {}

    def update_wemo_state(device):
        """ Update the state of specified WeMo device. """

        # We currently only support switches
        if not is_switch(device):
            return

        try:
            entity_id = sno_to_ent[device.serialnumber]

        except KeyError:
            # New device, set it up
            entity_id = util.ensure_unique_string(
                ENTITY_ID_FORMAT.format(util.slugify(device.name)),
                list(ent_to_dev.keys()))

            sno_to_ent[device.serialnumber] = entity_id
            ent_to_dev[entity_id] = device

        state = STATE_ON if device.get_state(True) else STATE_OFF

        state_attr = {ATTR_FRIENDLY_NAME: device.name}

        if isinstance(device, pywemo.Insight):
            pass
            # Should work but doesn't..
            #state_attr[ATTR_TODAY_KWH] = device.today_kwh
            #state_attr[ATTR_CURRENT_POWER] = device.current_power
            #state_attr[ATTR_TODAY_ON_TIME] = device.today_on_time
            #state_attr[ATTR_TODAY_STANDBY_TIME] = device.today_standby_time

        hass.states.set(entity_id, state, state_attr)

    # pylint: disable=unused-argument
    def update_wemos_state(time, force_reload=False):
        """ Update states of all WeMo devices. """

        # First time this method gets called, force_reload should be True
        if force_reload or \
           datetime.now() - update_wemos_state.last_updated > \
           MIN_TIME_BETWEEN_SCANS:

            logger.info("Updating WeMo status")
            update_wemos_state.last_updated = datetime.now()

            for device in switches:
                update_wemo_state(device)

    update_wemos_state(None, True)

    # Track all wemos in a group
    group.setup_group(hass, GROUP_NAME_ALL_WEMOS, sno_to_ent.values(), False)

    def handle_wemo_service(service):
        """ Handles calls to the WeMo service. """
        devices = [ent_to_dev[entity_id] for entity_id
                   in extract_entity_ids(hass, service)
                   if entity_id in ent_to_dev]

        if not devices:
            devices = ent_to_dev.values()

        for device in devices:
            if service.service == SERVICE_TURN_ON:
                device.on()
            else:
                device.off()

            update_wemo_state(device)

    # Update WeMo state every 30 seconds
    hass.track_time_change(update_wemos_state, second=[0, 30])

    hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_wemo_service)

    hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_wemo_service)

    return True