def __init__(self, hass, device_scanner, error_scanning=None):
        self.states = hass.states

        self.device_scanner = device_scanner

        self.error_scanning = error_scanning or TIME_SPAN_FOR_ERROR_IN_SCANNING

        self.logger = logging.getLogger(__name__)

        self.lock = threading.Lock()

        # 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(hass, GROUP_NAME_ALL_DEVICES, self.device_entity_ids)
示例#2
0
    def test_setup(self):
        """ Test setup method. """
        self.assertTrue(
            group.setup(
                self.hass, {
                    group.DOMAIN: {
                        'second_group': {
                            'entities': 'light.Bowl, ' + self.group_entity_id,
                            'icon': 'mdi:work',
                            'view': True,
                        },
                        'test_group': 'hello.world,sensor.happy',
                    }
                }))

        group_state = self.hass.states.get(
            group.ENTITY_ID_FORMAT.format('second_group'))
        self.assertEqual(STATE_ON, group_state.state)
        self.assertEqual(set((self.group_entity_id, 'light.bowl')),
                         set(group_state.attributes['entity_id']))
        self.assertIsNone(group_state.attributes.get(group.ATTR_AUTO))
        self.assertEqual('mdi:work', group_state.attributes.get(ATTR_ICON))
        self.assertTrue(group_state.attributes.get(group.ATTR_VIEW))
        self.assertTrue(group_state.attributes.get(ATTR_HIDDEN))

        group_state = self.hass.states.get(
            group.ENTITY_ID_FORMAT.format('test_group'))
        self.assertEqual(STATE_UNKNOWN, group_state.state)
        self.assertEqual(set(('sensor.happy', 'hello.world')),
                         set(group_state.attributes['entity_id']))
        self.assertIsNone(group_state.attributes.get(group.ATTR_AUTO))
        self.assertIsNone(group_state.attributes.get(ATTR_ICON))
        self.assertIsNone(group_state.attributes.get(group.ATTR_VIEW))
        self.assertIsNone(group_state.attributes.get(ATTR_HIDDEN))
示例#3
0
    def test_setup(self):
        """ Test setup method. """
        self.assertTrue(
            group.setup(
                self.hass, {
                    group.DOMAIN: {
                        'second_group': '{},light.Bowl'.format(self.group_name)
                    }
                }))

        group_state = self.hass.states.get(
            group.ENTITY_ID_FORMAT.format('second_group'))

        self.assertEqual(STATE_ON, group_state.state)
        self.assertFalse(group_state.attributes[group.ATTR_AUTO])
    def test_setup(self):
        """ Test setup method. """
        self.assertTrue(
            group.setup(
                self.hass,
                {
                    group.DOMAIN: {
                        'second_group': '{},light.Bowl'.format(self.group_name)
                    }
                }))

        group_state = self.hass.states.get(
            group.ENTITY_ID_FORMAT.format('second_group'))

        self.assertEqual(STATE_ON, group_state.state)
        self.assertFalse(group_state.attributes[group.ATTR_AUTO])
示例#5
0
    def test_setup(self):
        """ Test setup method. """
        self.assertTrue(
            group.setup(
                self.hass,
                {
                    group.DOMAIN: {
                        'second_group': 'light.Bowl, ' + self.group_entity_id
                    }
                }))

        group_state = self.hass.states.get(
            group.ENTITY_ID_FORMAT.format('second_group'))

        self.assertEqual(STATE_ON, group_state.state)
        self.assertEqual(set((self.group_entity_id, 'light.bowl')),
                         set(group_state.attributes['entity_id']))
        self.assertFalse(group_state.attributes[group.ATTR_AUTO])
示例#6
0
    def test_setup(self):
        """Test setup method."""
        self.hass.states.set('light.Bowl', STATE_ON)
        self.hass.states.set('light.Ceiling', STATE_OFF)
        test_group = group.Group(
            self.hass, 'init_group', ['light.Bowl', 'light.Ceiling'], False)

        self.assertTrue(
            group.setup(
                self.hass,
                {
                    group.DOMAIN: {
                        'second_group': {
                            'entities': 'light.Bowl, ' + test_group.entity_id,
                            'icon': 'mdi:work',
                            'view': True,
                        },
                        'test_group': 'hello.world,sensor.happy',
                    }
                }))

        group_state = self.hass.states.get(
            group.ENTITY_ID_FORMAT.format('second_group'))
        self.assertEqual(STATE_ON, group_state.state)
        self.assertEqual(set((test_group.entity_id, 'light.bowl')),
                         set(group_state.attributes['entity_id']))
        self.assertIsNone(group_state.attributes.get(group.ATTR_AUTO))
        self.assertEqual('mdi:work',
                         group_state.attributes.get(ATTR_ICON))
        self.assertTrue(group_state.attributes.get(group.ATTR_VIEW))
        self.assertTrue(group_state.attributes.get(ATTR_HIDDEN))

        group_state = self.hass.states.get(
            group.ENTITY_ID_FORMAT.format('test_group'))
        self.assertEqual(STATE_UNKNOWN, group_state.state)
        self.assertEqual(set(('sensor.happy', 'hello.world')),
                         set(group_state.attributes['entity_id']))
        self.assertIsNone(group_state.attributes.get(group.ATTR_AUTO))
        self.assertIsNone(group_state.attributes.get(ATTR_ICON))
        self.assertIsNone(group_state.attributes.get(group.ATTR_VIEW))
        self.assertIsNone(group_state.attributes.get(ATTR_HIDDEN))
示例#7
0
def setup(hass, light_control):
    """ Exposes light control via statemachine and services. """

    logger = logging.getLogger(__name__)

    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(hass, GROUP_NAME_ALL_LIGHTS, light_to_ent.values())

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

    for dir_path in profile_paths:
        file_path = os.path.join(dir_path, LIGHT_PROFILES_FILE)

        if os.path.isfile(file_path):
            with open(file_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(
                            file_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