Beispiel #1
0
 def test_ensure_unique_string(self):
     """Test ensure_unique_string."""
     self.assertEqual(
         "Beer_3",
         util.ensure_unique_string("Beer", ["Beer", "Beer_2"]))
     self.assertEqual(
         "Beer",
         util.ensure_unique_string("Beer", ["Wine", "Soda"]))
Beispiel #2
0
    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)
Beispiel #3
0
def async_generate_entity_id(entity_id_format: str, name: Optional[str],
                             current_ids: Optional[List[str]]=None) -> str:
    """Generate a unique entity ID based on given entity IDs or used IDs."""
    name = (name or DEVICE_DEFAULT_NAME).lower()

    return ensure_unique_string(
        entity_id_format.format(slugify(name)), current_ids)
Beispiel #4
0
    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)
Beispiel #5
0
 def find_registration_name(self, data):
     """Find a registration name matching data or generate a unique one."""
     endpoint = data.get(ATTR_SUBSCRIPTION).get(ATTR_ENDPOINT)
     for key, registration in self.registrations.items():
         subscription = registration.get(ATTR_SUBSCRIPTION)
         if subscription.get(ATTR_ENDPOINT) == endpoint:
             return key
     return ensure_unique_string('unnamed device', self.registrations)
Beispiel #6
0
    def async_see(self, mac: str=None, dev_id: str=None, host_name: str=None,
                  location_name: str=None, gps: GPSType=None,
                  gps_accuracy=None, battery: str=None, attributes: dict=None):
        """Notify the device tracker that you see a device.

        This method is a coroutine.
        """
        if mac is None and dev_id is None:
            raise HomeAssistantError('Neither mac or device id passed in')
        elif mac is not None:
            mac = str(mac).upper()
            device = self.mac_to_dev.get(mac)
            if not device:
                dev_id = util.slugify(host_name or '') or util.slugify(mac)
        else:
            dev_id = cv.slug(str(dev_id).lower())
            device = self.devices.get(dev_id)

        if device:
            yield from device.async_seen(host_name, location_name, gps,
                                         gps_accuracy, battery, attributes)
            if device.track:
                yield from device.async_update_ha_state()
            return

        # If no device can be found, create it
        dev_id = util.ensure_unique_string(dev_id, self.devices.keys())
        device = Device(
            self.hass, self.consider_home, self.track_new,
            dev_id, mac, (host_name or dev_id).replace('_', ' '))
        self.devices[dev_id] = device
        if mac is not None:
            self.mac_to_dev[mac] = device

        yield from device.async_seen(host_name, location_name, gps,
                                     gps_accuracy, battery, attributes)

        if device.track:
            yield from device.async_update_ha_state()

        self.hass.bus.async_fire(EVENT_NEW_DEVICE, {
            ATTR_ENTITY_ID: device.entity_id,
            ATTR_HOST_NAME: device.host_name,
        })

        # During init, we ignore the group
        if self.group is not None:
            yield from self.group.async_update_tracked_entity_ids(
                list(self.group.tracking) + [device.entity_id])

        # lookup mac vendor string to be stored in config
        yield from device.set_vendor_for_mac()

        # update known_devices.yaml
        self.hass.async_add_job(
            self.async_update_config(self.hass.config.path(YAML_DEVICES),
                                     dev_id, device)
        )
Beispiel #7
0
def generate_entity_id(entity_id_format, name, current_ids=None, hass=None):
    """ Generate a unique entity ID based on given entity IDs or used ids. """
    if current_ids is None:
        if hass is None:
            raise RuntimeError("Missing required parameter currentids or hass")

        current_ids = hass.states.entity_ids()

    return ensure_unique_string(
        entity_id_format.format(slugify(name.lower())), current_ids)
Beispiel #8
0
    def async_generate_entity_id(self, domain, suggested_object_id):
        """Generate an entity ID that does not conflict.

        Conflicts checked against registered and currently existing entities.
        """
        return ensure_unique_string(
            '{}.{}'.format(domain, slugify(suggested_object_id)),
            chain(self.entities.keys(),
                  self.hass.states.async_entity_ids(domain))
        )
Beispiel #9
0
def generate_entity_id(entity_id_format, name, current_ids=None, hass=None):
    """Generate a unique entity ID based on given entity IDs or used IDs."""
    name = (name or DEVICE_DEFAULT_NAME).lower()
    if current_ids is None:
        if hass is None:
            raise ValueError("Missing required parameter currentids or hass")

        current_ids = hass.states.entity_ids()

    return ensure_unique_string(
        entity_id_format.format(slugify(name)), current_ids)
Beispiel #10
0
def async_generate_entity_id(entity_id_format: str, name: Optional[str],
                             current_ids: Optional[List[str]]=None,
                             hass: Optional[HomeAssistant]=None) -> str:
    """Generate a unique entity ID based on given entity IDs or used IDs."""
    if current_ids is None:
        if hass is None:
            raise ValueError("Missing required parameter currentids or hass")

        current_ids = hass.states.async_entity_ids()
    name = (name or DEVICE_DEFAULT_NAME).lower()

    return ensure_unique_string(
        entity_id_format.format(slugify(name)), current_ids)
Beispiel #11
0
def convert_csv_config(csv_path, yaml_path):
    """ Convert CSV config file format to YAML. """
    used_ids = set()
    with open(csv_path) as inp:
        for row in csv.DictReader(inp):
            dev_id = util.ensure_unique_string(
                (util.slugify(row['name']) or DEVICE_DEFAULT_NAME).lower(),
                used_ids)
            used_ids.add(dev_id)
            device = Device(None, None, None, row['track'] == '1', dev_id,
                            row['device'], row['name'], row['picture'])
            update_config(yaml_path, dev_id, device)
    return True
Beispiel #12
0
def async_generate_entity_id(entity_id_format: str,
                             name: Optional[str],
                             current_ids: Optional[List[str]] = None,
                             hass: Optional[HomeAssistant] = None) -> str:
    """Generate a unique entity ID based on given entity IDs or used IDs."""
    if current_ids is None:
        if hass is None:
            raise ValueError("Missing required parameter currentids or hass")

        current_ids = hass.states.async_entity_ids()
    name = (name or DEVICE_DEFAULT_NAME).lower()

    return ensure_unique_string(entity_id_format.format(slugify(name)),
                                current_ids)
Beispiel #13
0
    def async_generate_entity_id(
        self, domain, suggested_object_id, known_object_ids=None
    ):
        """Generate an entity ID that does not conflict.

        Conflicts checked against registered and currently existing entities.
        """
        return ensure_unique_string(
            "{}.{}".format(domain, slugify(suggested_object_id)),
            chain(
                self.entities.keys(),
                self.hass.states.async_entity_ids(domain),
                known_object_ids if known_object_ids else [],
            ),
        )
Beispiel #14
0
def generate_entity_id(entity_id_format: str,
                       name: Optional[str],
                       current_ids: Optional[List[str]] = None,
                       hass: Optional[HomeAssistant] = None) -> str:
    """Generate a unique entity ID based on given entity IDs or used IDs."""
    if current_ids is None:
        if hass is None:
            raise ValueError("Missing required parameter currentids or hass")
        return run_callback_threadsafe(hass.loop, async_generate_entity_id,
                                       entity_id_format, name, current_ids,
                                       hass).result()

    name = (slugify(name) or slugify(DEVICE_DEFAULT_NAME)).lower()

    return ensure_unique_string(entity_id_format.format(name), current_ids)
Beispiel #15
0
    def see(self,
            mac: str = None,
            dev_id: str = None,
            host_name: str = None,
            location_name: str = None,
            gps: GPSType = None,
            gps_accuracy=None,
            battery: str = None,
            attributes: dict = None):
        """Notify the device tracker that you see a device."""
        with self.lock:
            if mac is None and dev_id is None:
                raise HomeAssistantError('Neither mac or device id passed in')
            elif mac is not None:
                mac = str(mac).upper()
                device = self.mac_to_dev.get(mac)
                if not device:
                    dev_id = util.slugify(host_name or '') or util.slugify(mac)
            else:
                dev_id = cv.slug(str(dev_id).lower())
                device = self.devices.get(dev_id)

            if device:
                device.seen(host_name, location_name, gps, gps_accuracy,
                            battery, attributes)
                if device.track:
                    device.update_ha_state()
                return

            # If no device can be found, create it
            dev_id = util.ensure_unique_string(dev_id, self.devices.keys())
            device = Device(self.hass, self.consider_home, self.track_new,
                            dev_id, mac, (host_name
                                          or dev_id).replace('_', ' '))
            self.devices[dev_id] = device
            if mac is not None:
                self.mac_to_dev[mac] = device

            device.seen(host_name, location_name, gps, gps_accuracy, battery,
                        attributes)
            if device.track:
                device.update_ha_state()

            # During init, we ignore the group
            if self.group is not None:
                self.group.update_tracked_entity_ids(
                    list(self.group.tracking) + [device.entity_id])
            update_config(self.hass.config.path(YAML_DEVICES), dev_id, device)
Beispiel #16
0
    def __init__(self, hass, name, entity_ids=None, user_defined=True):
        self.hass = hass
        self.name = name
        self.user_defined = user_defined

        self.entity_id = util.ensure_unique_string(
            ENTITY_ID_FORMAT.format(util.slugify(name)),
            hass.states.entity_ids(DOMAIN))

        self.tracking = []
        self.group_on, self.group_off = None, None

        if entity_ids is not None:
            self.update_tracked_entity_ids(entity_ids)
        else:
            self.force_update()
Beispiel #17
0
def generate_entity_id(entity_id_format: str, name: Optional[str],
                       current_ids: Optional[List[str]] = None,
                       hass: Optional[HomeAssistant] = None) -> str:
    """Generate a unique entity ID based on given entity IDs or used IDs."""
    if current_ids is None:
        if hass is None:
            raise ValueError("Missing required parameter currentids or hass")
        return run_callback_threadsafe(
            hass.loop, async_generate_entity_id, entity_id_format, name,
            current_ids, hass
        ).result()

    name = (slugify(name) or slugify(DEVICE_DEFAULT_NAME)).lower()

    return ensure_unique_string(
        entity_id_format.format(name), current_ids)
Beispiel #18
0
    def _generate_entity_ids(self, need_entity_id):
        """ Generate entity ids for a list of devices. """
        # Setup entity_ids for the new devices
        used_entity_ids = [info['entity_id'] for device, info
                           in self.tracked.items()
                           if device not in need_entity_id]

        for device in need_entity_id:
            name = self.tracked[device]['name']

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

            used_entity_ids.append(entity_id)

            self.tracked[device]['entity_id'] = entity_id
Beispiel #19
0
    def _generate_entity_ids(self, need_entity_id):
        """ Generate entity ids for a list of devices. """
        # Setup entity_ids for the new devices
        used_entity_ids = [info['entity_id'] for device, info
                           in self.tracked.items()
                           if device not in need_entity_id]

        for device in need_entity_id:
            name = self.tracked[device]['name']

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

            used_entity_ids.append(entity_id)

            self.tracked[device]['entity_id'] = entity_id
Beispiel #20
0
    def sensor_discovered(service, info):
        """ Called when a sensor is discovered. """
        platform = get_component("{}.{}".format(DOMAIN,
                                                DISCOVERY_PLATFORMS[service]))

        discovered = platform.devices_discovered(hass, config, info)

        for sensor in discovered:
            if sensor is not None and sensor not in sensors.values():
                sensor.entity_id = util.ensure_unique_string(
                    ENTITY_ID_FORMAT.format(util.slugify(sensor.name)),
                    sensors.keys())

                sensors[sensor.entity_id] = sensor

                sensor.update_ha_state(hass)

        sensor_group.update_tracked_entity_ids(sensors.keys())
Beispiel #21
0
    def async_generate_entity_id(
        self,
        domain: str,
        suggested_object_id: str,
        known_object_ids: Optional[Iterable[str]] = None,
    ) -> str:
        """Generate an entity ID that does not conflict.

        Conflicts checked against registered and currently existing entities.
        """
        return ensure_unique_string(
            f"{domain}.{slugify(suggested_object_id)}",
            chain(
                self.entities.keys(),
                self.hass.states.async_entity_ids(domain),
                known_object_ids if known_object_ids else [],
            ),
        )
Beispiel #22
0
    def post(self, request):
        """Accept the POST request for push registrations from a browser."""
        try:
            data = REGISTER_SCHEMA(request.json)
        except vol.Invalid as ex:
            return self.json_message(humanize_error(request.json, ex),
                                     HTTP_BAD_REQUEST)

        name = ensure_unique_string('unnamed device',
                                    self.registrations.keys())

        self.registrations[name] = data

        if not _save_config(self.json_path, self.registrations):
            return self.json_message('Error saving registration.',
                                     HTTP_INTERNAL_SERVER_ERROR)

        return self.json_message('Push notification subscriber registered.')
Beispiel #23
0
    def post(self, request):
        """Accept the POST request for push registrations from a browser."""
        try:
            data = REGISTER_SCHEMA(request.json)
        except vol.Invalid as ex:
            return self.json_message(humanize_error(request.json, ex),
                                     HTTP_BAD_REQUEST)

        name = ensure_unique_string('unnamed device',
                                    self.registrations.keys())

        self.registrations[name] = data

        if not _save_config(self.json_path, self.registrations):
            return self.json_message('Error saving registration.',
                                     HTTP_INTERNAL_SERVER_ERROR)

        return self.json_message('Push notification subscriber registered.')
Beispiel #24
0
    def sensor_discovered(service, info):
        """ Called when a sensor is discovered. """
        platform = get_component("{}.{}".format(
            DOMAIN, DISCOVERY_PLATFORMS[service]))

        discovered = platform.devices_discovered(hass, config, info)

        for sensor in discovered:
            if sensor is not None and sensor not in sensors.values():
                sensor.entity_id = util.ensure_unique_string(
                    ENTITY_ID_FORMAT.format(util.slugify(sensor.name)),
                    sensors.keys())

                sensors[sensor.entity_id] = sensor

                sensor.update_ha_state(hass)

        sensor_group.update_tracked_entity_ids(sensors.keys())
Beispiel #25
0
def setup_chromecast(casts, host):
    """ Tries to convert host to Chromecast object and set it up. """

    # Check if already setup
    if any(cast.host == host for cast in casts.values()):
        return

    try:
        cast = pychromecast.PyChromecast(host)

        entity_id = util.ensure_unique_string(
            ENTITY_ID_FORMAT.format(
                util.slugify(cast.device.friendly_name)),
            casts.keys())

        casts[entity_id] = cast

    except pychromecast.ChromecastConnectionError:
        pass
Beispiel #26
0
    def see(self, mac=None, dev_id=None, host_name=None, location_name=None, gps=None, gps_accuracy=None, battery=None):
        """Notify the device tracker that you see a device."""
        with self.lock:
            if mac is None and dev_id is None:
                raise HomeAssistantError("Neither mac or device id passed in")
            elif mac is not None:
                mac = mac.upper()
                device = self.mac_to_dev.get(mac)
                if not device:
                    dev_id = util.slugify(host_name or "") or util.slugify(mac)
            else:
                dev_id = cv.slug(str(dev_id).lower())
                device = self.devices.get(dev_id)

            if device:
                device.seen(host_name, location_name, gps, gps_accuracy, battery)
                if device.track:
                    device.update_ha_state()
                return

            # If no device can be found, create it
            dev_id = util.ensure_unique_string(dev_id, self.devices.keys())
            device = Device(
                self.hass,
                self.consider_home,
                self.home_range,
                self.track_new,
                dev_id,
                mac,
                (host_name or dev_id).replace("_", " "),
            )
            self.devices[dev_id] = device
            if mac is not None:
                self.mac_to_dev[mac] = device

            device.seen(host_name, location_name, gps, gps_accuracy, battery)
            if device.track:
                device.update_ha_state()

            # During init, we ignore the group
            if self.group is not None:
                self.group.update_tracked_entity_ids(list(self.group.tracking) + [device.entity_id])
            update_config(self.hass.config.path(YAML_DEVICES), dev_id, device)
Beispiel #27
0
def async_generate_entity_id(
    entity_id_format: str,
    name: str | None,
    current_ids: Iterable[str] | None = None,
    hass: HomeAssistant | None = None,
) -> str:
    """Generate a unique entity ID based on given entity IDs or used IDs."""
    name = (name or DEVICE_DEFAULT_NAME).lower()
    preferred_string = entity_id_format.format(slugify(name))

    if current_ids is not None:
        return ensure_unique_string(preferred_string, current_ids)

    if hass is None:
        raise ValueError("Missing required parameter current_ids or hass")

    test_string = preferred_string
    tries = 1
    while not hass.states.async_available(test_string):
        tries += 1
        test_string = f"{preferred_string}_{tries}"

    return test_string
Beispiel #28
0
def test_ensure_unique_string():
    """Test ensure_unique_string."""
    assert util.ensure_unique_string("Beer", ["Beer", "Beer_2"]) == "Beer_3"
    assert util.ensure_unique_string("Beer", ["Wine", "Soda"]) == "Beer"
Beispiel #29
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
Beispiel #30
0
    async def async_see(
            self, mac: str = None, dev_id: str = None, host_name: str = None,
            location_name: str = None, gps: GPSType = None,
            gps_accuracy: int = None, battery: int = None,
            attributes: dict = None, source_type: str = SOURCE_TYPE_GPS,
            picture: str = None, icon: str = None,
            consider_home: timedelta = None):
        """Notify the device tracker that you see a device.

        This method is a coroutine.
        """
        if mac is None and dev_id is None:
            raise HomeAssistantError('Neither mac or device id passed in')
        if mac is not None:
            mac = str(mac).upper()
            device = self.mac_to_dev.get(mac)
            if not device:
                dev_id = util.slugify(host_name or '') or util.slugify(mac)
        else:
            dev_id = cv.slug(str(dev_id).lower())
            device = self.devices.get(dev_id)

        if device:
            await device.async_seen(
                host_name, location_name, gps, gps_accuracy, battery,
                attributes, source_type, consider_home)
            if device.track:
                await device.async_update_ha_state()
            return

        # If no device can be found, create it
        dev_id = util.ensure_unique_string(dev_id, self.devices.keys())
        device = Device(
            self.hass, consider_home or self.consider_home, self.track_new,
            dev_id, mac, (host_name or dev_id).replace('_', ' '),
            picture=picture, icon=icon,
            hide_if_away=self.defaults.get(CONF_AWAY_HIDE, DEFAULT_AWAY_HIDE))
        self.devices[dev_id] = device
        if mac is not None:
            self.mac_to_dev[mac] = device

        await device.async_seen(
            host_name, location_name, gps, gps_accuracy, battery, attributes,
            source_type)

        if device.track:
            await device.async_update_ha_state()

        # During init, we ignore the group
        if self.group and self.track_new:
            self.hass.async_create_task(
                self.hass.async_call(
                    DOMAIN_GROUP, SERVICE_SET, {
                        ATTR_OBJECT_ID: util.slugify(GROUP_NAME_ALL_DEVICES),
                        ATTR_VISIBLE: False,
                        ATTR_NAME: GROUP_NAME_ALL_DEVICES,
                        ATTR_ADD_ENTITIES: [device.entity_id]}))

        self.hass.bus.async_fire(EVENT_NEW_DEVICE, {
            ATTR_ENTITY_ID: device.entity_id,
            ATTR_HOST_NAME: device.host_name,
            ATTR_MAC: device.mac,
        })

        # update known_devices.yaml
        self.hass.async_create_task(
            self.async_update_config(
                self.hass.config.path(YAML_DEVICES), dev_id, device)
        )
Beispiel #31
0
    def _read_known_devices_file(self):
        """ Parse and process the known devices file. """
        known_dev_path = self.hass.get_config_path(KNOWN_DEVICES_FILE)

        # Return if no known devices file exists
        if not os.path.isfile(known_dev_path):
            return

        self.lock.acquire()

        self.untracked_devices.clear()

        with open(known_dev_path) as inp:
            default_last_seen = datetime(1990, 1, 1)

            # To track which devices need an entity_id assigned
            need_entity_id = []

            # All devices that are still in this set after we read the CSV file
            # have been removed from the file and thus need to be cleaned up.
            removed_devices = set(self.tracked.keys())

            try:
                for row in csv.DictReader(inp):
                    device = row['device']

                    if row['track'] == '1':
                        if device in self.tracked:
                            # Device exists
                            removed_devices.remove(device)
                        else:
                            # We found a new device
                            need_entity_id.append(device)

                            self.tracked[device] = {
                                'name': row['name'],
                                'last_seen': default_last_seen
                            }

                        # Update state_attr with latest from file
                        state_attr = {ATTR_FRIENDLY_NAME: row['name']}

                        if row['picture']:
                            state_attr[ATTR_ENTITY_PICTURE] = row['picture']

                        self.tracked[device]['state_attr'] = state_attr

                    else:
                        self.untracked_devices.add(device)

                # Remove existing devices that we no longer track
                for device in removed_devices:
                    entity_id = self.tracked[device]['entity_id']

                    _LOGGER.info("Removing entity %s", entity_id)

                    self.hass.states.remove(entity_id)

                    self.tracked.pop(device)

                # Setup entity_ids for the new devices
                used_entity_ids = [
                    info['entity_id'] for device, info in self.tracked.items()
                    if device not in need_entity_id
                ]

                for device in need_entity_id:
                    name = self.tracked[device]['name']

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

                    used_entity_ids.append(entity_id)

                    self.tracked[device]['entity_id'] = entity_id

                if not self.tracked:
                    _LOGGER.warning("No devices to track. Please update %s.",
                                    known_dev_path)

                _LOGGER.info("Loaded devices from %s", known_dev_path)

            except KeyError:
                self.invalid_known_devices_file = True

                _LOGGER.warning(("Invalid known devices file: %s. "
                                 "We won't update it with new found devices."),
                                known_dev_path)

            finally:
                self.lock.release()
Beispiel #32
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
Beispiel #33
0
    def async_see(self, mac: str = None, dev_id: str = None,
                  host_name: str = None, location_name: str = None,
                  gps: GPSType = None, gps_accuracy=None, battery: str = None,
                  attributes: dict = None, source_type: str = SOURCE_TYPE_GPS,
                  picture: str = None, icon: str = None):
        """Notify the device tracker that you see a device.

        This method is a coroutine.
        """
        if mac is None and dev_id is None:
            raise HomeAssistantError('Neither mac or device id passed in')
        elif mac is not None:
            mac = str(mac).upper()
            device = self.mac_to_dev.get(mac)
            if not device:
                dev_id = util.slugify(host_name or '') or util.slugify(mac)
        else:
            dev_id = cv.slug(str(dev_id).lower())
            device = self.devices.get(dev_id)

        if device:
            yield from device.async_seen(
                host_name, location_name, gps, gps_accuracy, battery,
                attributes, source_type)
            if device.track:
                yield from device.async_update_ha_state()
            return

        # If no device can be found, create it
        dev_id = util.ensure_unique_string(dev_id, self.devices.keys())
        device = Device(
            self.hass, self.consider_home, self.track_new,
            dev_id, mac, (host_name or dev_id).replace('_', ' '),
            picture=picture, icon=icon,
            hide_if_away=self.defaults.get(CONF_AWAY_HIDE, DEFAULT_AWAY_HIDE))
        self.devices[dev_id] = device
        if mac is not None:
            self.mac_to_dev[mac] = device

        yield from device.async_seen(
            host_name, location_name, gps, gps_accuracy, battery, attributes,
            source_type)

        if device.track:
            yield from device.async_update_ha_state()

        # During init, we ignore the group
        if self.group and self.track_new:
            self.group.async_set_group(
                self.hass, util.slugify(GROUP_NAME_ALL_DEVICES), visible=False,
                name=GROUP_NAME_ALL_DEVICES, add=[device.entity_id])

        # lookup mac vendor string to be stored in config
        yield from device.set_vendor_for_mac()

        self.hass.bus.async_fire(EVENT_NEW_DEVICE, {
            ATTR_ENTITY_ID: device.entity_id,
            ATTR_HOST_NAME: device.host_name,
            ATTR_MAC: device.mac,
            ATTR_VENDOR: device.vendor,
        })

        # update known_devices.yaml
        self.hass.async_add_job(
            self.async_update_config(
                self.hass.config.path(YAML_DEVICES), dev_id, device)
        )
Beispiel #34
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
Beispiel #35
0
    def async_see(self,
                  mac: str = None,
                  dev_id: str = None,
                  host_name: str = None,
                  location_name: str = None,
                  gps: GPSType = None,
                  gps_accuracy=None,
                  battery: str = None,
                  attributes: dict = None,
                  source_type: str = SOURCE_TYPE_GPS):
        """Notify the device tracker that you see a device.

        This method is a coroutine.
        """
        if mac is None and dev_id is None:
            raise HomeAssistantError('Neither mac or device id passed in')
        elif mac is not None:
            mac = str(mac).upper()
            device = self.mac_to_dev.get(mac)
            if not device:
                dev_id = util.slugify(host_name or '') or util.slugify(mac)
        else:
            dev_id = cv.slug(str(dev_id).lower())
            device = self.devices.get(dev_id)

        if device:
            yield from device.async_seen(host_name, location_name, gps,
                                         gps_accuracy, battery, attributes,
                                         source_type)
            if device.track:
                yield from device.async_update_ha_state()
            return

        # If no device can be found, create it
        dev_id = util.ensure_unique_string(dev_id, self.devices.keys())
        device = Device(self.hass, self.consider_home, self.track_new, dev_id,
                        mac, (host_name or dev_id).replace('_', ' '))
        self.devices[dev_id] = device
        if mac is not None:
            self.mac_to_dev[mac] = device

        yield from device.async_seen(host_name, location_name, gps,
                                     gps_accuracy, battery, attributes,
                                     source_type)

        if device.track:
            yield from device.async_update_ha_state()

        self.hass.bus.async_fire(EVENT_NEW_DEVICE, {
            ATTR_ENTITY_ID: device.entity_id,
            ATTR_HOST_NAME: device.host_name,
        })

        # During init, we ignore the group
        if self.group is not None:
            yield from self.group.async_update_tracked_entity_ids(
                list(self.group.tracking) + [device.entity_id])

        # lookup mac vendor string to be stored in config
        yield from device.set_vendor_for_mac()

        # update known_devices.yaml
        self.hass.async_add_job(
            self.async_update_config(self.hass.config.path(YAML_DEVICES),
                                     dev_id, device))
Beispiel #36
0
def setup(hass, config):
    """ Listen for chromecast events. """
    logger = logging.getLogger(__name__)

    try:
        import pychromecast
    except ImportError:
        logger.exception(("Failed to import pychromecast. "
                          "Did you maybe not install the 'pychromecast' "
                          "dependency?"))

        return False

    if CONF_HOSTS in config[DOMAIN]:
        hosts = config[DOMAIN][CONF_HOSTS].split(",")

    # If no hosts given, scan for chromecasts
    else:
        logger.info("Scanning for Chromecasts")
        hosts = pychromecast.discover_chromecasts()

    casts = {}

    for host in hosts:
        try:
            cast = pychromecast.PyChromecast(host)

            entity_id = util.ensure_unique_string(
                ENTITY_ID_FORMAT.format(util.slugify(
                    cast.device.friendly_name)), casts.keys())

            casts[entity_id] = cast

        except pychromecast.ChromecastConnectionError:
            pass

    if not casts:
        logger.error("Could not find Chromecasts")
        return False

    def update_chromecast_state(entity_id, chromecast):
        """ Retrieve state of Chromecast and update statemachine. """
        chromecast.refresh()

        status = chromecast.app

        state_attr = {ATTR_FRIENDLY_NAME: chromecast.device.friendly_name}

        if status and status.app_id != pychromecast.APP_ID['HOME']:
            state = status.app_id

            ramp = chromecast.get_protocol(pychromecast.PROTOCOL_RAMP)

            if ramp and ramp.state != pychromecast.RAMP_STATE_UNKNOWN:

                if ramp.state == pychromecast.RAMP_STATE_PLAYING:
                    state_attr[ATTR_MEDIA_STATE] = MEDIA_STATE_PLAYING
                else:
                    state_attr[ATTR_MEDIA_STATE] = MEDIA_STATE_STOPPED

                if ramp.content_id:
                    state_attr[ATTR_MEDIA_CONTENT_ID] = ramp.content_id

                if ramp.title:
                    state_attr[ATTR_MEDIA_TITLE] = ramp.title

                if ramp.artist:
                    state_attr[ATTR_MEDIA_ARTIST] = ramp.artist

                if ramp.album:
                    state_attr[ATTR_MEDIA_ALBUM] = ramp.album

                if ramp.image_url:
                    state_attr[ATTR_MEDIA_IMAGE_URL] = ramp.image_url

                if ramp.duration:
                    state_attr[ATTR_MEDIA_DURATION] = ramp.duration

                state_attr[ATTR_MEDIA_VOLUME] = ramp.volume
        else:
            state = STATE_NO_APP

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

    def update_chromecast_states(time):  # pylint: disable=unused-argument
        """ Updates all chromecast states. """
        logger.info("Updating Chromecast status")

        for entity_id, cast in casts.items():
            update_chromecast_state(entity_id, cast)

    def _service_to_entities(service):
        """ Helper method to get entities from service. """
        entity_ids = extract_entity_ids(hass, service)

        if entity_ids:
            for entity_id in entity_ids:
                cast = casts.get(entity_id)

                if cast:
                    yield entity_id, cast

        else:
            yield from casts.items()

    def turn_off_service(service):
        """ Service to exit any running app on the specified ChromeCast and
        shows idle screen. Will quit all ChromeCasts if nothing specified.
        """
        for entity_id, cast in _service_to_entities(service):
            cast.quit_app()
            update_chromecast_state(entity_id, cast)

    def volume_up_service(service):
        """ Service to send the chromecast the command for volume up. """
        for _, cast in _service_to_entities(service):
            ramp = cast.get_protocol(pychromecast.PROTOCOL_RAMP)

            if ramp:
                ramp.volume_up()

    def volume_down_service(service):
        """ Service to send the chromecast the command for volume down. """
        for _, cast in _service_to_entities(service):
            ramp = cast.get_protocol(pychromecast.PROTOCOL_RAMP)

            if ramp:
                ramp.volume_down()

    def media_play_pause_service(service):
        """ Service to send the chromecast the command for play/pause. """
        for _, cast in _service_to_entities(service):
            ramp = cast.get_protocol(pychromecast.PROTOCOL_RAMP)

            if ramp:
                ramp.playpause()

    def media_play_service(service):
        """ Service to send the chromecast the command for play/pause. """
        for _, cast in _service_to_entities(service):
            ramp = cast.get_protocol(pychromecast.PROTOCOL_RAMP)

            if ramp and ramp.state == pychromecast.RAMP_STATE_STOPPED:
                ramp.playpause()

    def media_pause_service(service):
        """ Service to send the chromecast the command for play/pause. """
        for _, cast in _service_to_entities(service):
            ramp = cast.get_protocol(pychromecast.PROTOCOL_RAMP)

            if ramp and ramp.state == pychromecast.RAMP_STATE_PLAYING:
                ramp.playpause()

    def media_next_track_service(service):
        """ Service to send the chromecast the command for next track. """
        for entity_id, cast in _service_to_entities(service):
            ramp = cast.get_protocol(pychromecast.PROTOCOL_RAMP)

            if ramp:
                next(ramp)
                update_chromecast_state(entity_id, cast)

    def play_youtube_video_service(service, video_id):
        """ Plays specified video_id on the Chromecast's YouTube channel. """
        if video_id:  # if service.data.get('video') returned None
            for entity_id, cast in _service_to_entities(service):
                pychromecast.play_youtube_video(video_id, cast.host)
                update_chromecast_state(entity_id, cast)

    hass.track_time_change(update_chromecast_states)

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

    hass.services.register(DOMAIN, SERVICE_VOLUME_UP, volume_up_service)

    hass.services.register(DOMAIN, SERVICE_VOLUME_DOWN, volume_down_service)

    hass.services.register(DOMAIN, SERVICE_MEDIA_PLAY_PAUSE,
                           media_play_pause_service)

    hass.services.register(DOMAIN, SERVICE_MEDIA_PLAY, media_play_service)

    hass.services.register(DOMAIN, SERVICE_MEDIA_PAUSE, media_pause_service)

    hass.services.register(DOMAIN, SERVICE_MEDIA_NEXT_TRACK,
                           media_next_track_service)

    hass.services.register(
        DOMAIN, "start_fireplace",
        lambda service: play_youtube_video_service(service, "eyU3bRy2x44"))

    hass.services.register(
        DOMAIN, "start_epic_sax",
        lambda service: play_youtube_video_service(service, "kxopViU98Xo"))

    hass.services.register(
        DOMAIN, SERVICE_YOUTUBE_VIDEO,
        lambda service: play_youtube_video_service(service,
                                                   service.data.get('video')))

    update_chromecast_states(None)

    return True
Beispiel #37
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
Beispiel #38
0
    async def async_see(self,
                        mac: str = None,
                        dev_id: str = None,
                        host_name: str = None,
                        location_name: str = None,
                        gps: GPSType = None,
                        gps_accuracy: int = None,
                        battery: int = None,
                        attributes: dict = None,
                        source_type: str = SOURCE_TYPE_GPS,
                        picture: str = None,
                        icon: str = None,
                        consider_home: timedelta = None):
        """Notify the device tracker that you see a device.

        This method is a coroutine.
        """
        if mac is None and dev_id is None:
            raise HomeAssistantError('Neither mac or device id passed in')
        elif mac is not None:
            mac = str(mac).upper()
            device = self.mac_to_dev.get(mac)
            if not device:
                dev_id = util.slugify(host_name or '') or util.slugify(mac)
        else:
            dev_id = cv.slug(str(dev_id).lower())
            device = self.devices.get(dev_id)

        if device:
            await device.async_seen(host_name, location_name, gps,
                                    gps_accuracy, battery, attributes,
                                    source_type, consider_home)
            if device.track:
                await device.async_update_ha_state()
            return

        # If no device can be found, create it
        dev_id = util.ensure_unique_string(dev_id, self.devices.keys())
        device = Device(self.hass,
                        consider_home or self.consider_home,
                        self.track_new,
                        dev_id,
                        mac, (host_name or dev_id).replace('_', ' '),
                        picture=picture,
                        icon=icon,
                        hide_if_away=self.defaults.get(CONF_AWAY_HIDE,
                                                       DEFAULT_AWAY_HIDE))
        self.devices[dev_id] = device
        if mac is not None:
            self.mac_to_dev[mac] = device

        await device.async_seen(host_name, location_name, gps, gps_accuracy,
                                battery, attributes, source_type)

        if device.track:
            await device.async_update_ha_state()

        # During init, we ignore the group
        if self.group and self.track_new:
            self.hass.async_create_task(
                self.hass.async_call(
                    DOMAIN_GROUP, SERVICE_SET, {
                        ATTR_OBJECT_ID: util.slugify(GROUP_NAME_ALL_DEVICES),
                        ATTR_VISIBLE: False,
                        ATTR_NAME: GROUP_NAME_ALL_DEVICES,
                        ATTR_ADD_ENTITIES: [device.entity_id]
                    }))

        self.hass.bus.async_fire(
            EVENT_NEW_DEVICE, {
                ATTR_ENTITY_ID: device.entity_id,
                ATTR_HOST_NAME: device.host_name,
                ATTR_MAC: device.mac,
            })

        # update known_devices.yaml
        self.hass.async_create_task(
            self.async_update_config(self.hass.config.path(YAML_DEVICES),
                                     dev_id, device))
Beispiel #39
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
Beispiel #40
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
Beispiel #41
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
Beispiel #42
0
 def test_ensure_unique_string(self):
     """Test ensure_unique_string."""
     assert "Beer_3" == \
         util.ensure_unique_string("Beer", ["Beer", "Beer_2"])
     assert "Beer" == \
         util.ensure_unique_string("Beer", ["Wine", "Soda"])
Beispiel #43
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
Beispiel #44
0
            if device.track:
                device.async_write_ha_state()
            return

        # If it's None then device is not None and we can't get here.
        assert dev_id is not None

        # Guard from calling see on entity registry entities.
        entity_id = f"{DOMAIN}.{dev_id}"
        if registry.async_is_registered(entity_id):
            LOGGER.error("The see service is not supported for this entity %s",
                         entity_id)
            return

        # If no device can be found, create it
        dev_id = util.ensure_unique_string(dev_id, self.devices.keys())
        device = Device(
            self.hass,
            consider_home or self.consider_home,
            self.track_new,
            dev_id,
            mac,
            picture=picture,
            icon=icon,
        )
        self.devices[dev_id] = device
        if mac is not None:
            self.mac_to_dev[mac] = device

        await device.async_seen(
            host_name,
Beispiel #45
0
    async def async_see(
        self,
        mac: str = None,
        dev_id: str = None,
        host_name: str = None,
        location_name: str = None,
        gps: GPSType = None,
        gps_accuracy: int = None,
        battery: int = None,
        attributes: dict = None,
        source_type: str = SOURCE_TYPE_GPS,
        picture: str = None,
        icon: str = None,
        consider_home: timedelta = None,
    ):
        """Notify the device tracker that you see a device.

        This method is a coroutine.
        """
        registry = await async_get_registry(self.hass)
        if mac is None and dev_id is None:
            raise HomeAssistantError("Neither mac or device id passed in")
        if mac is not None:
            mac = str(mac).upper()
            device = self.mac_to_dev.get(mac)
            if not device:
                dev_id = util.slugify(host_name or "") or util.slugify(mac)
        else:
            dev_id = cv.slug(str(dev_id).lower())
            device = self.devices.get(dev_id)

        if device:
            await device.async_seen(
                host_name,
                location_name,
                gps,
                gps_accuracy,
                battery,
                attributes,
                source_type,
                consider_home,
            )
            if device.track:
                await device.async_update_ha_state()
            return

        # Guard from calling see on entity registry entities.
        entity_id = f"{DOMAIN}.{dev_id}"
        if registry.async_is_registered(entity_id):
            LOGGER.error("The see service is not supported for this entity %s",
                         entity_id)
            return

        # If no device can be found, create it
        dev_id = util.ensure_unique_string(dev_id, self.devices.keys())
        device = Device(
            self.hass,
            consider_home or self.consider_home,
            self.track_new,
            dev_id,
            mac,
            picture=picture,
            icon=icon,
        )
        self.devices[dev_id] = device
        if mac is not None:
            self.mac_to_dev[mac] = device

        await device.async_seen(
            host_name,
            location_name,
            gps,
            gps_accuracy,
            battery,
            attributes,
            source_type,
        )

        if device.track:
            await device.async_update_ha_state()

        self.hass.bus.async_fire(
            EVENT_NEW_DEVICE,
            {
                ATTR_ENTITY_ID: device.entity_id,
                ATTR_HOST_NAME: device.host_name,
                ATTR_MAC: device.mac,
            },
        )

        # update known_devices.yaml
        self.hass.async_create_task(
            self.async_update_config(self.hass.config.path(YAML_DEVICES),
                                     dev_id, device))
Beispiel #46
0
 def test_ensure_unique_string(self):
     """Test ensure_unique_string."""
     assert "Beer_3" == \
         util.ensure_unique_string("Beer", ["Beer", "Beer_2"])
     assert "Beer" == \
         util.ensure_unique_string("Beer", ["Wine", "Soda"])
 def test_ensure_unique_string(self):
     """Test ensure_unique_string."""
     self.assertEqual("Beer_3",
                      util.ensure_unique_string("Beer", ["Beer", "Beer_2"]))
     self.assertEqual("Beer",
                      util.ensure_unique_string("Beer", ["Wine", "Soda"]))
Beispiel #48
0
def setup(hass, config):
    """ Listen for chromecast events. """
    logger = logging.getLogger(__name__)

    try:
        import pychromecast
    except ImportError:
        logger.exception(("Failed to import pychromecast. "
                          "Did you maybe not install the 'pychromecast' "
                          "dependency?"))

        return False

    if 'hosts' in config[DOMAIN]:
        hosts = config[DOMAIN]['hosts'].split(",")

    # If no hosts given, scan for chromecasts
    else:
        logger.info("Scanning for Chromecasts")
        hosts = pychromecast.discover_chromecasts()

    casts = {}

    for host in hosts:
        try:
            cast = pychromecast.PyChromecast(host)

            entity_id = util.ensure_unique_string(
                ENTITY_ID_FORMAT.format(
                    util.slugify(cast.device.friendly_name)),
                list(casts.keys()))

            casts[entity_id] = cast

        except pychromecast.ChromecastConnectionError:
            pass

    if not casts:
        logger.error("Could not find Chromecasts")
        return False

    def update_chromecast_state(entity_id, chromecast):
        """ Retrieve state of Chromecast and update statemachine. """
        chromecast.refresh()

        status = chromecast.app

        state_attr = {components.ATTR_FRIENDLY_NAME:
                      chromecast.device.friendly_name}

        if status and status.app_id != pychromecast.APP_ID['HOME']:
            state = status.app_id

            ramp = chromecast.get_protocol(pychromecast.PROTOCOL_RAMP)

            if ramp and ramp.state != pychromecast.RAMP_STATE_UNKNOWN:

                if ramp.state == pychromecast.RAMP_STATE_PLAYING:
                    state_attr[ATTR_MEDIA_STATE] = MEDIA_STATE_PLAYING
                else:
                    state_attr[ATTR_MEDIA_STATE] = MEDIA_STATE_STOPPED

                if ramp.content_id:
                    state_attr[ATTR_MEDIA_CONTENT_ID] = ramp.content_id

                if ramp.title:
                    state_attr[ATTR_MEDIA_TITLE] = ramp.title

                if ramp.artist:
                    state_attr[ATTR_MEDIA_ARTIST] = ramp.artist

                if ramp.album:
                    state_attr[ATTR_MEDIA_ALBUM] = ramp.album

                if ramp.image_url:
                    state_attr[ATTR_MEDIA_IMAGE_URL] = ramp.image_url

                if ramp.duration:
                    state_attr[ATTR_MEDIA_DURATION] = ramp.duration

                state_attr[ATTR_MEDIA_VOLUME] = ramp.volume
        else:
            state = STATE_NO_APP

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

    def update_chromecast_states(time):  # pylint: disable=unused-argument
        """ Updates all chromecast states. """
        logger.info("Updating Chromecast status")

        for entity_id, cast in casts.items():
            update_chromecast_state(entity_id, cast)

    def _service_to_entities(service):
        """ Helper method to get entities from service. """
        entity_ids = components.extract_entity_ids(hass, service)

        if entity_ids:
            for entity_id in entity_ids:
                cast = casts.get(entity_id)

                if cast:
                    yield entity_id, cast

        else:
            yield from casts.items()

    def turn_off_service(service):
        """ Service to exit any running app on the specified ChromeCast and
        shows idle screen. Will quit all ChromeCasts if nothing specified.
        """
        for entity_id, cast in _service_to_entities(service):
            cast.quit_app()
            update_chromecast_state(entity_id, cast)

    def volume_up_service(service):
        """ Service to send the chromecast the command for volume up. """
        for _, cast in _service_to_entities(service):
            ramp = cast.get_protocol(pychromecast.PROTOCOL_RAMP)

            if ramp:
                ramp.volume_up()

    def volume_down_service(service):
        """ Service to send the chromecast the command for volume down. """
        for _, cast in _service_to_entities(service):
            ramp = cast.get_protocol(pychromecast.PROTOCOL_RAMP)

            if ramp:
                ramp.volume_down()

    def media_play_pause_service(service):
        """ Service to send the chromecast the command for play/pause. """
        for _, cast in _service_to_entities(service):
            ramp = cast.get_protocol(pychromecast.PROTOCOL_RAMP)

            if ramp:
                ramp.playpause()

    def media_play_service(service):
        """ Service to send the chromecast the command for play/pause. """
        for _, cast in _service_to_entities(service):
            ramp = cast.get_protocol(pychromecast.PROTOCOL_RAMP)

            if ramp and ramp.state == pychromecast.RAMP_STATE_STOPPED:
                ramp.playpause()

    def media_pause_service(service):
        """ Service to send the chromecast the command for play/pause. """
        for _, cast in _service_to_entities(service):
            ramp = cast.get_protocol(pychromecast.PROTOCOL_RAMP)

            if ramp and ramp.state == pychromecast.RAMP_STATE_PLAYING:
                ramp.playpause()

    def media_next_track_service(service):
        """ Service to send the chromecast the command for next track. """
        for entity_id, cast in _service_to_entities(service):
            ramp = cast.get_protocol(pychromecast.PROTOCOL_RAMP)

            if ramp:
                next(ramp)
                update_chromecast_state(entity_id, cast)

    def play_youtube_video_service(service, video_id):
        """ Plays specified video_id on the Chromecast's YouTube channel. """
        if video_id:  # if service.data.get('video') returned None
            for entity_id, cast in _service_to_entities(service):
                pychromecast.play_youtube_video(video_id, cast.host)
                update_chromecast_state(entity_id, cast)

    hass.track_time_change(update_chromecast_states)

    hass.services.register(DOMAIN, components.SERVICE_TURN_OFF,
                           turn_off_service)

    hass.services.register(DOMAIN, components.SERVICE_VOLUME_UP,
                           volume_up_service)

    hass.services.register(DOMAIN, components.SERVICE_VOLUME_DOWN,
                           volume_down_service)

    hass.services.register(DOMAIN, components.SERVICE_MEDIA_PLAY_PAUSE,
                           media_play_pause_service)

    hass.services.register(DOMAIN, components.SERVICE_MEDIA_PLAY,
                           media_play_service)

    hass.services.register(DOMAIN, components.SERVICE_MEDIA_PAUSE,
                           media_pause_service)

    hass.services.register(DOMAIN, components.SERVICE_MEDIA_NEXT_TRACK,
                           media_next_track_service)

    hass.services.register(DOMAIN, "start_fireplace",
                           lambda service:
                           play_youtube_video_service(service, "eyU3bRy2x44"))

    hass.services.register(DOMAIN, "start_epic_sax",
                           lambda service:
                           play_youtube_video_service(service, "kxopViU98Xo"))

    hass.services.register(DOMAIN, SERVICE_YOUTUBE_VIDEO,
                           lambda service:
                           play_youtube_video_service(service,
                                                      service.data.get(
                                                          'video')))

    update_chromecast_states(None)

    return True
Beispiel #49
0
    def _read_known_devices_file(self):
        """ Parse and process the known devices file. """
        known_dev_path = self.hass.config.path(KNOWN_DEVICES_FILE)

        # Return if no known devices file exists
        if not os.path.isfile(known_dev_path):
            return

        self.lock.acquire()

        self.untracked_devices.clear()

        with open(known_dev_path) as inp:
            default_last_seen = datetime(1990, 1, 1)

            # To track which devices need an entity_id assigned
            need_entity_id = []

            # All devices that are still in this set after we read the CSV file
            # have been removed from the file and thus need to be cleaned up.
            removed_devices = set(self.tracked.keys())

            try:
                for row in csv.DictReader(inp):
                    device = row['device'].upper()

                    if row['track'] == '1':
                        if device in self.tracked:
                            # Device exists
                            removed_devices.remove(device)
                        else:
                            # We found a new device
                            need_entity_id.append(device)

                            self.tracked[device] = {
                                'name': row['name'],
                                'last_seen': default_last_seen
                            }

                        # Update state_attr with latest from file
                        state_attr = {
                            ATTR_FRIENDLY_NAME: row['name']
                        }

                        if row['picture']:
                            state_attr[ATTR_ENTITY_PICTURE] = row['picture']

                        self.tracked[device]['state_attr'] = state_attr

                    else:
                        self.untracked_devices.add(device)

                # Remove existing devices that we no longer track
                for device in removed_devices:
                    entity_id = self.tracked[device]['entity_id']

                    _LOGGER.info("Removing entity %s", entity_id)

                    self.hass.states.remove(entity_id)

                    self.tracked.pop(device)

                # Setup entity_ids for the new devices
                used_entity_ids = [info['entity_id'] for device, info
                                   in self.tracked.items()
                                   if device not in need_entity_id]

                for device in need_entity_id:
                    name = self.tracked[device]['name']

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

                    used_entity_ids.append(entity_id)

                    self.tracked[device]['entity_id'] = entity_id

                if not self.tracked:
                    _LOGGER.warning(
                        "No devices to track. Please update %s.",
                        known_dev_path)

                _LOGGER.info("Loaded devices from %s", known_dev_path)

            except KeyError:
                self.invalid_known_devices_file = True

                _LOGGER.warning(
                    ("Invalid known devices file: %s. "
                     "We won't update it with new found devices."),
                    known_dev_path)

            finally:
                self.lock.release()