Beispiel #1
0
    async def async_step_hassio(
            self, discovery_info: HassioServiceInfo) -> FlowResult:
        """Prepare configuration for a Hass.io deCONZ bridge.

        This flow is triggered by the discovery component.
        """
        LOGGER.debug("deCONZ HASSIO discovery %s",
                     pformat(discovery_info.config))

        self.bridge_id = normalize_bridge_id(
            discovery_info.config[CONF_SERIAL])
        await self.async_set_unique_id(self.bridge_id)

        self.host = discovery_info.config[CONF_HOST]
        self.port = discovery_info.config[CONF_PORT]
        self.api_key = discovery_info.config[CONF_API_KEY]

        self._abort_if_unique_id_configured(
            updates={
                CONF_HOST: self.host,
                CONF_PORT: self.port,
                CONF_API_KEY: self.api_key,
            })

        self.context["configuration_url"] = HASSIO_CONFIGURATION_URL
        self._hassio_discovery = discovery_info.config

        return await self.async_step_hassio_confirm()
Beispiel #2
0
    async def async_step_ssdp(self, discovery_info):
        """Handle a discovered deCONZ bridge."""
        if (discovery_info.get(ssdp.ATTR_UPNP_MANUFACTURER_URL) !=
                DECONZ_MANUFACTURERURL):
            return self.async_abort(reason="not_deconz_bridge")

        LOGGER.debug("deCONZ SSDP discovery %s", pformat(discovery_info))

        self.bridge_id = normalize_bridge_id(
            discovery_info[ssdp.ATTR_UPNP_SERIAL])
        parsed_url = urlparse(discovery_info[ssdp.ATTR_SSDP_LOCATION])

        entry = await self.async_set_unique_id(self.bridge_id)
        if entry and entry.source == "hassio":
            return self.async_abort(reason="already_configured")

        self._abort_if_unique_id_configured(updates={
            CONF_HOST: parsed_url.hostname,
            CONF_PORT: parsed_url.port
        })

        self.context["title_placeholders"] = {"host": parsed_url.hostname}

        self.deconz_config = {
            CONF_HOST: parsed_url.hostname,
            CONF_PORT: parsed_url.port,
        }

        return await self.async_step_link()
Beispiel #3
0
    async def async_call_deconz_service(service_call: ServiceCall) -> None:
        """Call correct deCONZ service."""
        service = service_call.service
        service_data = service_call.data

        if CONF_BRIDGE_ID in service_data:
            found_gateway = False
            bridge_id = normalize_bridge_id(service_data[CONF_BRIDGE_ID])

            for possible_gateway in hass.data[DOMAIN].values():
                if possible_gateway.bridgeid == bridge_id:
                    gateway = possible_gateway
                    found_gateway = True
                    break

            if not found_gateway:
                LOGGER.error("Could not find the gateway %s", bridge_id)
                return
        else:
            try:
                gateway = get_master_gateway(hass)
            except ValueError:
                LOGGER.error("No master gateway available")
                return

        if service == SERVICE_CONFIGURE_DEVICE:
            await async_configure_service(gateway, service_data)

        elif service == SERVICE_DEVICE_REFRESH:
            await async_refresh_devices_service(gateway)

        elif service == SERVICE_REMOVE_ORPHANED_ENTRIES:
            await async_remove_orphaned_entries_service(gateway)
Beispiel #4
0
    async def async_step_ssdp(
            self, discovery_info: ssdp.SsdpServiceInfo) -> FlowResult:
        """Handle a discovered deCONZ bridge."""
        LOGGER.debug("deCONZ SSDP discovery %s", pformat(discovery_info))

        self.bridge_id = normalize_bridge_id(
            discovery_info.upnp[ssdp.ATTR_UPNP_SERIAL])
        parsed_url = urlparse(discovery_info.ssdp_location)

        entry = await self.async_set_unique_id(self.bridge_id)
        if entry and entry.source == config_entries.SOURCE_HASSIO:
            return self.async_abort(reason="already_configured")

        self.host = cast(str, parsed_url.hostname)
        self.port = cast(int, parsed_url.port)

        self._abort_if_unique_id_configured(updates={
            CONF_HOST: self.host,
            CONF_PORT: self.port,
        })

        self.context.update({
            "title_placeholders": {
                "host": self.host
            },
            "configuration_url":
            f"http://{self.host}:{self.port}",
        })

        return await self.async_step_link()
Beispiel #5
0
    async def async_step_ssdp(self, discovery_info):
        """Handle a discovered deCONZ bridge."""
        if (
            discovery_info.get(ssdp.ATTR_UPNP_MANUFACTURER_URL)
            != DECONZ_MANUFACTURERURL
        ):
            return self.async_abort(reason="not_deconz_bridge")

        self.bridge_id = normalize_bridge_id(discovery_info[ssdp.ATTR_UPNP_SERIAL])
        parsed_url = urlparse(discovery_info[ssdp.ATTR_SSDP_LOCATION])

        for entry in self.hass.config_entries.async_entries(DOMAIN):
            if self.bridge_id == entry.unique_id:
                if entry.source == "hassio":
                    return self.async_abort(reason="already_configured")
                return self._update_entry(entry, parsed_url.hostname, parsed_url.port)

        await self.async_set_unique_id(self.bridge_id)
        # pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167
        self.context["title_placeholders"] = {"host": parsed_url.hostname}

        self.deconz_config = {
            CONF_HOST: parsed_url.hostname,
            CONF_PORT: parsed_url.port,
        }

        return await self.async_step_link()
Beispiel #6
0
    async def async_step_ssdp(
            self, discovery_info: ssdp.SsdpServiceInfo) -> FlowResult:
        """Handle a discovered deCONZ bridge."""
        if (discovery_info.upnp.get(ssdp.ATTR_UPNP_MANUFACTURER_URL) !=
                DECONZ_MANUFACTURERURL):
            return self.async_abort(reason="not_deconz_bridge")

        LOGGER.debug("deCONZ SSDP discovery %s", pformat(discovery_info))

        self.bridge_id = normalize_bridge_id(
            discovery_info.upnp[ssdp.ATTR_UPNP_SERIAL])
        parsed_url = urlparse(discovery_info.ssdp_location)

        entry = await self.async_set_unique_id(self.bridge_id)
        if entry and entry.source == config_entries.SOURCE_HASSIO:
            return self.async_abort(reason="already_configured")

        hostname = cast(str, parsed_url.hostname)
        port = cast(int, parsed_url.port)

        self._abort_if_unique_id_configured(updates={
            CONF_HOST: hostname,
            CONF_PORT: port
        })

        self.context["title_placeholders"] = {"host": hostname}

        self.deconz_config = {CONF_HOST: hostname, CONF_PORT: port}

        return await self.async_step_link()
Beispiel #7
0
async def async_configure_service(hass, data):
    """Set attribute of device in deCONZ.

    Entity is used to resolve to a device path (e.g. '/lights/1').
    Field is a string representing either a full path
    (e.g. '/lights/1/state') when entity is not specified, or a
    subpath (e.g. '/state') when used together with entity.
    Data is a json object with what data you want to alter
    e.g. data={'on': true}.
    {
        "field": "/lights/1/state",
        "data": {"on": true}
    }
    See Dresden Elektroniks REST API documentation for details:
    http://dresden-elektronik.github.io/deconz-rest-doc/rest/
    """
    gateway = get_master_gateway(hass)
    if CONF_BRIDGE_ID in data:
        gateway = hass.data[DOMAIN][normalize_bridge_id(data[CONF_BRIDGE_ID])]

    field = data.get(SERVICE_FIELD, "")
    entity_id = data.get(SERVICE_ENTITY)
    data = data[SERVICE_DATA]

    if entity_id:
        try:
            field = gateway.deconz_ids[entity_id] + field
        except KeyError:
            LOGGER.error("Could not find the entity %s", entity_id)
            return

    await gateway.api.request("put", field, json=data)
Beispiel #8
0
async def async_remove_orphaned_entries_service(hass, data):
    """Remove orphaned deCONZ entries from device and entity registries."""
    gateway = get_master_gateway(hass)
    if CONF_BRIDGE_ID in data:
        gateway = hass.data[DOMAIN][normalize_bridge_id(data[CONF_BRIDGE_ID])]

    entity_registry = await hass.helpers.entity_registry.async_get_registry()
    device_registry = await hass.helpers.device_registry.async_get_registry()

    entity_entries = async_entries_for_config_entry(
        entity_registry, gateway.config_entry.entry_id)

    entities_to_be_removed = []
    devices_to_be_removed = [
        entry.id for entry in device_registry.devices.values()
        if gateway.config_entry.entry_id in entry.config_entries
    ]

    # Don't remove the Gateway host entry
    gateway_host = device_registry.async_get_device(
        connections={(CONNECTION_NETWORK_MAC, gateway.api.config.mac)},
        identifiers=set(),
    )
    if gateway_host.id in devices_to_be_removed:
        devices_to_be_removed.remove(gateway_host.id)

    # Don't remove the Gateway service entry
    gateway_service = device_registry.async_get_device(identifiers={
        (DOMAIN, gateway.api.config.bridgeid)
    },
                                                       connections=set())
    if gateway_service.id in devices_to_be_removed:
        devices_to_be_removed.remove(gateway_service.id)

    # Don't remove devices belonging to available events
    for event in gateway.events:
        if event.device_id in devices_to_be_removed:
            devices_to_be_removed.remove(event.device_id)

    for entry in entity_entries:

        # Don't remove available entities
        if entry.unique_id in gateway.entities[entry.domain]:

            # Don't remove devices with available entities
            if entry.device_id in devices_to_be_removed:
                devices_to_be_removed.remove(entry.device_id)
            continue
        # Remove entities that are not available
        entities_to_be_removed.append(entry.entity_id)

    # Remove unavailable entities
    for entity_id in entities_to_be_removed:
        entity_registry.async_remove(entity_id)

    # Remove devices that don't belong to any entity
    for device_id in devices_to_be_removed:
        if len(async_entries_for_device(entity_registry, device_id)) == 0:
            device_registry.async_remove_device(device_id)
Beispiel #9
0
async def async_refresh_devices_service(hass, data):
    """Refresh available devices from deCONZ."""
    gateway = get_master_gateway(hass)
    if CONF_BRIDGE_ID in data:
        gateway = hass.data[DOMAIN][normalize_bridge_id(data[CONF_BRIDGE_ID])]

    gateway.ignore_state_updates = True
    await gateway.api.refresh_state()
    gateway.ignore_state_updates = False

    gateway.async_add_device_callback(NEW_GROUP)
    gateway.async_add_device_callback(NEW_LIGHT)
    gateway.async_add_device_callback(NEW_SCENE)
    gateway.async_add_device_callback(NEW_SENSOR)
Beispiel #10
0
async def async_refresh_devices_service(hass, data):
    """Refresh available devices from deCONZ."""
    gateway = get_master_gateway(hass)
    if CONF_BRIDGE_ID in data:
        gateway = hass.data[DOMAIN][normalize_bridge_id(data[CONF_BRIDGE_ID])]

    groups = set(gateway.api.groups.keys())
    lights = set(gateway.api.lights.keys())
    scenes = set(gateway.api.scenes.keys())
    sensors = set(gateway.api.sensors.keys())

    await gateway.api.refresh_state(ignore_update=True)

    gateway.async_add_device_callback(
        NEW_GROUP,
        [
            group
            for group_id, group in gateway.api.groups.items()
            if group_id not in groups
        ],
    )

    gateway.async_add_device_callback(
        NEW_LIGHT,
        [
            light
            for light_id, light in gateway.api.lights.items()
            if light_id not in lights
        ],
    )

    gateway.async_add_device_callback(
        NEW_SCENE,
        [
            scene
            for scene_id, scene in gateway.api.scenes.items()
            if scene_id not in scenes
        ],
    )

    gateway.async_add_device_callback(
        NEW_SENSOR,
        [
            sensor
            for sensor_id, sensor in gateway.api.sensors.items()
            if sensor_id not in sensors
        ],
    )
Beispiel #11
0
    async def async_step_hassio(self, user_input=None):
        """Prepare configuration for a Hass.io deCONZ bridge.

        This flow is triggered by the discovery component.
        """
        self.bridge_id = normalize_bridge_id(user_input[CONF_SERIAL])
        await self.async_set_unique_id(self.bridge_id)

        self._abort_if_unique_id_configured(
            updates={
                CONF_HOST: user_input[CONF_HOST],
                CONF_PORT: user_input[CONF_PORT],
                CONF_API_KEY: user_input[CONF_API_KEY],
            }
        )

        self._hassio_discovery = user_input

        return await self.async_step_hassio_confirm()
Beispiel #12
0
    async def async_step_hassio(self, discovery_info):
        """Prepare configuration for a Hass.io deCONZ bridge.

        This flow is triggered by the discovery component.
        """
        LOGGER.debug("deCONZ HASSIO discovery %s", pformat(discovery_info))

        self.bridge_id = normalize_bridge_id(discovery_info[CONF_SERIAL])
        await self.async_set_unique_id(self.bridge_id)

        self._abort_if_unique_id_configured(
            updates={
                CONF_HOST: discovery_info[CONF_HOST],
                CONF_PORT: discovery_info[CONF_PORT],
                CONF_API_KEY: discovery_info[CONF_API_KEY],
            })

        self._hassio_discovery = discovery_info

        return await self.async_step_hassio_confirm()
Beispiel #13
0
    async def async_step_hassio(self, user_input=None):
        """Prepare configuration for a Hass.io deCONZ bridge.

        This flow is triggered by the discovery component.
        """
        self.bridge_id = normalize_bridge_id(user_input[CONF_SERIAL])

        for entry in self.hass.config_entries.async_entries(DOMAIN):
            if self.bridge_id == entry.unique_id:
                return self._update_entry(
                    entry,
                    user_input[CONF_HOST],
                    user_input[CONF_PORT],
                    user_input[CONF_API_KEY],
                )

        await self.async_set_unique_id(self.bridge_id)
        self._hassio_discovery = user_input

        return await self.async_step_hassio_confirm()
Beispiel #14
0
    async def async_step_hassio(self, user_input=None):
        """Prepare configuration for a Hass.io deCONZ bridge.

        This flow is triggered by the discovery component.
        """
        self.bridge_id = normalize_bridge_id(user_input[CONF_SERIAL])
        gateway = self.hass.data.get(DOMAIN, {}).get(self.bridge_id)

        if gateway:
            return self._update_entry(
                gateway.config_entry,
                user_input[CONF_HOST],
                user_input[CONF_PORT],
                user_input[CONF_API_KEY],
            )

        await self.async_set_unique_id(self.bridge_id)
        self._hassio_discovery = user_input

        return await self.async_step_hassio_confirm()