Ejemplo n.º 1
0
    async def async_step_user(
        self, user_input: dict[str, Any] | None = None
    ) -> dict[str, Any]:
        """Handle a flow initialized by the user."""

        if user_input is None:
            return self._show_config_form()

        try:
            # Cannot use cv.url validation in the schema itself, so
            # apply extra validation here.
            cv.url(user_input[CONF_URL])
        except vol.Invalid:
            return self._show_config_form(user_input, errors={"base": "invalid_url"})

        try:
            session = async_create_clientsession(self.hass)
            client = FrigateApiClient(user_input[CONF_URL], session)
            await client.async_get_stats()
        except FrigateApiClientError:
            return self._show_config_form(user_input, errors={"base": "cannot_connect"})

        # Search for duplicates with the same Frigate CONF_HOST value.
        for existing_entry in self._async_current_entries(include_ignore=False):
            if existing_entry.data.get(CONF_URL) == user_input[CONF_URL]:
                return cast(
                    Dict[str, Any], self.async_abort(reason="already_configured")
                )

        return cast(
            Dict[str, Any],
            self.async_create_entry(
                title=get_config_entry_title(user_input[CONF_URL]), data=user_input
            ),
        )
Ejemplo n.º 2
0
    async def async_step_options_misc(self, user_input=None):
        """Allow the user to configure the LED behavior."""
        errors = {}
        if user_input is not None:
            # config schema only does basic schema val so check url here
            try:
                if user_input[CONF_OVERRIDE_API_HOST]:
                    cv.url(user_input.get(CONF_API_HOST, ""))
                else:
                    user_input[CONF_API_HOST] = ""
            except vol.Invalid:
                errors["base"] = "bad_host"
            else:
                # no need to store the override - can infer
                del user_input[CONF_OVERRIDE_API_HOST]
                self.new_opt.update(user_input)
                return self.async_create_entry(title="", data=self.new_opt)

        return self.async_show_form(
            step_id="options_misc",
            data_schema=vol.Schema({
                vol.Required(CONF_BLINK,
                             default=self.current_opt.get(CONF_BLINK, True)):
                bool,
                vol.Required(
                    CONF_OVERRIDE_API_HOST,
                    default=bool(self.current_opt.get(CONF_API_HOST)),
                ):
                bool,
                vol.Optional(CONF_API_HOST,
                             default=self.current_opt.get(CONF_API_HOST, "")):
                str,
            }),
            errors=errors,
        )
Ejemplo n.º 3
0
    async def async_play_media(self, media_type, media_id, **kwargs):
        """Support changing a channel."""

        # Type channel
        if media_type == MEDIA_TYPE_CHANNEL:
            try:
                cv.positive_int(media_id)
            except vol.Invalid:
                _LOGGER.error("Media ID must be positive integer")
                return

            def send_digit():
                for digit in media_id:
                    self.send_command("KEY_" + digit)
                    sleep(KEYPRESS_DEFAULT_DELAY)
                self.send_command("KEY_ENTER")
            await self.hass.async_add_executor_job(send_digit)

        # Launch an app
        elif media_type == MEDIA_TYPE_APP:
            await self.async_send_command(media_id, "run_app")

        # Send custom key
        elif media_type == MEDIA_TYPE_KEY:
            try:
                cv.string(media_id)
            except vol.Invalid:
                _LOGGER.error('Media ID must be a string (ex: "KEY_HOME"')
                return

            source_key = media_id
            await self._async_send_keys(source_key)

        # Play media
        elif media_type == MEDIA_TYPE_URL:
            try:
                cv.url(media_id)
            except vol.Invalid:
                _LOGGER.error('Media ID must be an url (ex: "http://"')
                return

            await self._upnp.async_set_current_media(media_id)
            self._playing = True

        # Trying to make stream component work on TV
        elif media_type == "application/vnd.apple.mpegurl":
            await self._upnp.async_set_current_media(media_id)
            self._playing = True

        elif media_type == MEDIA_TYPE_BROWSER:
            await self.hass.async_add_executor_job(
                self._ws.open_browser, media_id
            )

        else:
            _LOGGER.error("Unsupported media type")
            return
Ejemplo n.º 4
0
    async def async_step_init(
        self, user_input: dict[str, Any] | None = None
    ) -> FlowResult:
        """Manage the options."""
        errors: dict[str, str] = {}
        # Don't modify existing (read-only) options -- copy and update instead
        options = dict(self.config_entry.options)

        if user_input is not None:
            LOGGER.debug("user_input: %s", user_input)
            listen_port = user_input.get(CONF_LISTEN_PORT) or None
            callback_url_override = user_input.get(CONF_CALLBACK_URL_OVERRIDE) or None

            try:
                # Cannot use cv.url validation in the schema itself so apply
                # extra validation here
                if callback_url_override:
                    cv.url(callback_url_override)
            except vol.Invalid:
                errors["base"] = "invalid_url"

            options[CONF_LISTEN_PORT] = listen_port
            options[CONF_CALLBACK_URL_OVERRIDE] = callback_url_override
            options[CONF_POLL_AVAILABILITY] = user_input[CONF_POLL_AVAILABILITY]

            # Save if there's no errors, else fall through and show the form again
            if not errors:
                return self.async_create_entry(title="", data=options)

        fields = {}

        def _add_with_suggestion(key: str, validator: Callable) -> None:
            """Add a field to with a suggested, not default, value."""
            suggested_value = options.get(key)
            if suggested_value is None:
                fields[vol.Optional(key)] = validator
            else:
                fields[
                    vol.Optional(key, description={"suggested_value": suggested_value})
                ] = validator

        # listen_port can be blank or 0 for "bind any free port"
        _add_with_suggestion(CONF_LISTEN_PORT, cv.port)
        _add_with_suggestion(CONF_CALLBACK_URL_OVERRIDE, str)
        fields[
            vol.Required(
                CONF_POLL_AVAILABILITY,
                default=options.get(CONF_POLL_AVAILABILITY, False),
            )
        ] = bool

        return self.async_show_form(
            step_id="init",
            data_schema=vol.Schema(fields),
            errors=errors,
        )
Ejemplo n.º 5
0
    async def async_play_media(self, media_type, media_id, **kwargs):
        """Support changing a channel."""

        # Type channel
        if media_type == MEDIA_TYPE_CHANNEL:
            try:
                cv.positive_int(media_id)
            except vol.Invalid:
                _LOGGER.error("Media ID must be positive integer")
                return

            for digit in media_id:
                await self.hass.async_add_job(self.send_command,
                                              "KEY_" + digit)

            await self.hass.async_add_job(self.send_command, "KEY_ENTER")

        # Launch an app
        elif media_type == MEDIA_TYPE_APP:
            await self.hass.async_add_job(self.send_command, media_id,
                                          "run_app")

        # Send custom key
        elif media_type == MEDIA_TYPE_KEY:
            try:
                cv.string(media_id)
            except vol.Invalid:
                _LOGGER.error('Media ID must be a string (ex: "KEY_HOME"')
                return

            await self.hass.async_add_job(self.send_command, media_id)

        # Play media
        elif media_type == MEDIA_TYPE_URL:
            try:
                cv.url(media_id)
            except vol.Invalid:
                _LOGGER.error('Media ID must be an url (ex: "http://"')
                return

            await self.hass.async_add_job(self._upnp.set_current_media,
                                          media_id)
            self._playing = True

        # Trying to make stream component work on TV
        elif media_type == "application/vnd.apple.mpegurl":
            await self.hass.async_add_job(self._upnp.set_current_media,
                                          media_id)
            self._playing = True

        else:
            _LOGGER.error("Unsupported media type: " + media_type)
            return
Ejemplo n.º 6
0
    async def async_play_media(self, media_type, media_id, **kwargs):
        """Support running different media type command."""
        media_type = media_type.lower()

        # Type channel
        if media_type == MEDIA_TYPE_CHANNEL:
            await self._async_set_channel(media_id)

        # Launch an app
        elif media_type == MEDIA_TYPE_APP:
            await self._async_launch_app(media_id)

        # Send custom key
        elif media_type == MEDIA_TYPE_KEY:
            try:
                cv.string(media_id)
            except vol.Invalid:
                _LOGGER.error('Media ID must be a string (ex: "KEY_HOME"')
                return

            source_key = media_id
            await self._async_send_keys(source_key)

        # Play media
        elif media_type == MEDIA_TYPE_URL:
            try:
                cv.url(media_id)
            except vol.Invalid:
                _LOGGER.error('Media ID must be an url (ex: "http://"')
                return

            await self._upnp.async_set_current_media(media_id)
            self._playing = True

        # Trying to make stream component work on TV
        elif media_type == "application/vnd.apple.mpegurl":
            await self._upnp.async_set_current_media(media_id)
            self._playing = True

        elif media_type == MEDIA_TYPE_BROWSER:
            await self.async_send_command(media_id, CMD_OPEN_BROWSER)

        elif media_type == MEDIA_TYPE_TEXT:
            await self.async_send_command(media_id, CMD_SEND_TEXT)

        else:
            _LOGGER.error("Unsupported media type: %s", media_type)
            return
    async def async_step_user(self, user_input=None):
        """Handle a flow initialized by the user."""
        errors = {}
        if user_input is not None:
            try:
                cv.url(user_input.get(CONF_URL, ""))
                self._init_info[CONF_URL] = user_input[CONF_URL]
                return await self.async_step_fetch_cities(
                    url=user_input.get(CONF_URL, ""))
            except vol.Invalid:
                errors["base"] = "bad_host"

        return self.async_show_form(
            step_id="user",
            errors=errors,
            data_schema=vol.Schema({vol.Required(CONF_URL, default=""): str}))
Ejemplo n.º 8
0
def valid_base_url(value: str) -> str:
    """Validate base url, return value."""
    url = yarl.URL(cv.url(value))

    if url.path != "/":
        raise vol.Invalid("Path should be empty")

    return normalize_url(value)
Ejemplo n.º 9
0
    async def async_step_init(
        self, user_input: dict[str, Any] | None = None
    ) -> FlowResult:
        """Manage the options."""
        errors: dict[str, str] = {}
        # Don't modify existing (read-only) options -- copy and update instead
        options = dict(self.config_entry.options)

        if user_input is not None:
            LOGGER.debug("user_input: %s", user_input)
            listen_port = user_input.get(CONF_LISTEN_PORT) or None
            callback_url_override = user_input.get(CONF_CALLBACK_URL_OVERRIDE) or None

            try:
                # Cannot use cv.url validation in the schema itself so apply
                # extra validation here
                if callback_url_override:
                    cv.url(callback_url_override)
            except vol.Invalid:
                errors["base"] = "invalid_url"

            options[CONF_LISTEN_PORT] = listen_port
            options[CONF_CALLBACK_URL_OVERRIDE] = callback_url_override
            options[CONF_POLL_AVAILABILITY] = user_input[CONF_POLL_AVAILABILITY]
            options[CONF_BROWSE_UNFILTERED] = user_input[CONF_BROWSE_UNFILTERED]

            # Save if there's no errors, else fall through and show the form again
            if not errors:
                return self.async_create_entry(title="", data=options)

        fields = {}

        def _add_with_suggestion(key: str, validator: Callable | type[bool]) -> None:
            """Add a field to with a suggested value.

            For bools, use the existing value as default, or fallback to False.
            """
            if validator is bool:
                fields[vol.Required(key, default=options.get(key, False))] = validator
            elif (suggested_value := options.get(key)) is None:
                fields[vol.Optional(key)] = validator
            else:
Ejemplo n.º 10
0
    async def async_play_media(self, media_type, media_id, **kwargs):
        if media_type == MEDIA_TYPE_CHANNEL:
            try:
                cv.positive_int(media_id)
            except vol.Invalid:
                _LOGGER.error("Media ID must be positive integer")
                return

            for digit in media_id:
                await self._tizenws.send_key("KEY_" + digit,
                                             KEYPRESS_DEFAULT_DELAY)

            await self._tizenws.send_ke("KEY_ENTER")
        if media_type == MEDIA_TYPE_APP:
            await self._tizenws.run_app(media_id)
        elif media_type == MEDIA_TYPE_KEY:
            try:
                cv.string(media_id)
            except vol.Invalid:
                _LOGGER.error('Media ID must be a string (ex: "KEY_HOME"')
                return

            #     source_key = media_id
            await self._tizenws.send_key(media_id)
        elif media_type == MEDIA_TYPE_URL:
            try:
                cv.url(media_id)
            except vol.Invalid:
                _LOGGER.error('Media ID must be an url (ex: "http://"')
                return

            await self._upnp.async_set_current_media(media_id)
        elif media_type == "application/vnd.apple.mpegurl":
            await self._upnp.async_set_current_media(media_id)
        # elif media_type == MEDIA_TYPE_BROWSER:
        #     self._tizenws.open_browser(media_id)
        else:
            _LOGGER.error("Unsupported media type")
            return

        self.async_schedule_update_ha_state(True)
Ejemplo n.º 11
0
async def _validate_input(user_input: Dict[str, Any]) -> Dict[str, Any]:
    """Validate that the user input can create a working connection."""

    # data has the keys from CONFIG_SCHEMA with values provided by the user.
    host: str = user_input[CONF_HOST]
    app_id: str = user_input[CONF_APP_ID]
    token: str = user_input[CONF_ACCESS_TOKEN]
    port: Optional[int] = user_input.get(CONF_SERVER_PORT)
    event_url: Optional[str] = user_input.get(CONF_SERVER_URL)

    if event_url:
        event_url = cv.url(event_url)

    hub = HubitatHub(host, app_id, token, port=port, event_url=event_url)
    await hub.check_config()

    return {"label": f"Hubitat ({get_hub_short_id(hub)})", "hub": hub}
Ejemplo n.º 12
0
 async def async_play_media(self, media_type, media_id, **kwargs):
     """Support changing a channel."""
     # Type channel
     if media_type == MEDIA_TYPE_CHANNEL:
         try:
             cv.positive_int(media_id)
         except vol.Invalid:
             _LOGGER.error("Media ID must be positive integer")
             return
         for digit in media_id:
             await self.hass.async_add_job(self.send_command,
                                           "KEY_" + digit)
         await self.hass.async_add_job(self.send_command, "KEY_ENTER")
     # Launch an app
     elif media_type == MEDIA_TYPE_APP:
         await self.hass.async_add_job(self.send_command, media_id,
                                       "run_app")
     # Send custom key
     elif media_type == MEDIA_TYPE_KEY:
         try:
             cv.string(media_id)
         except vol.Invalid:
             _LOGGER.error('Media ID must be a string (ex: "KEY_HOME"')
             return
         source_key = media_id
         if "+" in source_key:
             all_source_keys = source_key.split("+")
             for this_key in all_source_keys:
                 if this_key.isdigit():
                     time.sleep(int(this_key) / 1000)
                 else:
                     if this_key.startswith("ST_"):
                         await self.hass.async_add_job(
                             self._smartthings_keys, this_key)
                     else:
                         await self.hass.async_add_job(
                             self.send_command, this_key)
         elif source_key.startswith("ST_"):
             await self.hass.async_add_job(self._smartthings_keys,
                                           source_key)
         else:
             await self.hass.async_add_job(self.send_command, source_key)
     # Play media
     elif media_type == MEDIA_TYPE_URL:
         try:
             cv.url(media_id)
         except vol.Invalid:
             _LOGGER.error('Media ID must be an url (ex: "http://"')
             return
         self._upnp.set_current_media(media_id)
         self._playing = True
     # Trying to make stream component work on TV
     elif media_type == "application/vnd.apple.mpegurl":
         self._upnp.set_current_media(media_id)
         self._playing = True
     elif media_type == MEDIA_TYPE_BROWSER:
         try:
             self._ws.open_browser(media_id)
         except (ConnectionResetError, AttributeError, BrokenPipeError,
                 websocket._exceptions.WebSocketTimeoutException):
             self._ws.close()
     else:
         _LOGGER.error("Unsupported media type")
         return
Ejemplo n.º 13
0
    async def async_play_media(self, media_type, media_id, **kwargs):
        """Support changing a channel."""

        # Type channel
        if media_type == MEDIA_TYPE_CHANNEL:
            try:
                cv.positive_int(media_id)
            except vol.Invalid:
                _LOGGER.error("Media ID must be positive integer")
                return

            for digit in media_id:
                await self.hass.async_add_job(self.send_command,
                                              "KEY_" + digit)

            await self.hass.async_add_job(self.send_command, "KEY_ENTER")

        # Launch an app
        elif media_type == MEDIA_TYPE_APP:
            await self.hass.async_add_job(self.send_command, media_id,
                                          "run_app")

        # Send custom key
        elif media_type == MEDIA_TYPE_KEY:
            try:
                cv.string(media_id)
            except vol.Invalid:
                _LOGGER.error('Media ID must be a string (ex: "KEY_HOME"')
                return

            source_key = media_id

            if source_key.startswith("ST_"):
                if source_key.startswith("ST_HDMI"):
                    smartthings.send_command(self,
                                             source_key.replace("ST_", ""),
                                             "selectsource")
                elif source_key == "ST_TV":
                    smartthings.send_command(self, "digitalTv", "selectsource")
            elif "+" in source_key:
                all_source_keys = source_key.split("+")
                for this_key in all_source_keys:
                    if this_key.isdigit():
                        time.sleep(int(this_key) / 1000)
                    else:
                        await self.hass.async_add_job(self.send_command,
                                                      this_key)
            else:
                await self.hass.async_add_job(self.send_command, source_key)

        # Play media
        elif media_type == MEDIA_TYPE_URL:
            try:
                cv.url(media_id)
            except vol.Invalid:
                _LOGGER.error('Media ID must be an url (ex: "http://"')
                return

            self._upnp.set_current_media(media_id)
            self._playing = True

        # Trying to make stream component work on TV
        elif media_type == "application/vnd.apple.mpegurl":
            self._upnp.set_current_media(media_id)
            self._playing = True

        else:
            _LOGGER.error("Unsupported media type")
            return
Ejemplo n.º 14
0
async def _start_config_flow(
    cls: Union[RentalControlFlowHandler, RentalControlOptionsFlow],
    step_id: str,
    title: str,
    user_input: Dict[str, Any],
    defaults: Dict[str, Any] = None,
    entry_id: str = None,
):
    """Start a config flow."""
    errors = {}
    description_placeholders = {}

    if user_input is not None:
        # Regular flow has an async function
        if hasattr(cls, "_get_unique_id"):
            errors.update(await cls._get_unique_id(user_input))

        # Validate user input
        try:
            cv.url(user_input["url"])
            # We require that the URL be an SSL URL
            if not re.search("^https://", user_input["url"]):
                errors["base"] = "invalid_url"
            else:
                session = async_get_clientsession(
                    cls.hass, verify_ssl=user_input["verify_ssl"])
                with async_timeout.timeout(REQUEST_TIMEOUT):
                    resp = await session.get(user_input["url"])
                if resp.status != 200:
                    _LOGGER.error(
                        "%s returned %s - %s",
                        user_input["url"],
                        resp.status,
                        resp.reason,
                    )
                    errors["base"] = "unknown"
                else:
                    # We require text/calendar in the content-type header
                    if "text/calendar" not in resp.content_type:
                        errors["base"] = "bad_ics"
        except vol.Invalid as err:
            _LOGGER.exception(err.msg)
            errors["base"] = "invalid_url"

        if user_input[CONF_REFRESH_FREQUENCY] > 1440:
            errors["base"] = "bad_refresh"

        try:
            cv.time(user_input["checkin"])
            cv.time(user_input["checkout"])
        except vol.Invalid as err:
            _LOGGER.exception(err.msg)
            errors["base"] = "bad_time"

        if not errors:
            # Only do this conversion if there are no errors and it needs to be
            # done. Doing this before the errors check will lead to later
            # validation issues should the user not reset the lock entry
            # Convert (none) to None
            if user_input[CONF_LOCK_ENTRY] == "(none)":
                user_input[CONF_LOCK_ENTRY] = None

            # Convert code generator to proper type
            user_input[CONF_CODE_GENERATION] = _generator_convert(
                ident=user_input[CONF_CODE_GENERATION], to_type=True)

            if hasattr(cls, "created"):
                user_input[CONF_CREATION_DATETIME] = cls.created

            return cls.async_create_entry(title=title, data=user_input)

        return _show_config_form(
            cls,
            step_id,
            user_input,
            errors,
            description_placeholders,
            defaults,
            entry_id,
        )

    return _show_config_form(
        cls,
        step_id,
        user_input,
        errors,
        description_placeholders,
        defaults,
        entry_id,
    )
Ejemplo n.º 15
0
    async def async_step_user(self,
                              user_input: dict[str, Any] | None = None
                              ) -> FlowResult:
        """Handle the initial step."""
        def _get_form(user_input: dict[str, Any],
                      errors: dict[str, str] | None = None) -> FlowResult:
            """Show the form to the user."""
            url_schema: dict[vol.Required, type[str]] = {}
            if not self._hassio_discovery:
                # Only ask for URL when not discovered
                url_schema[vol.Required(CONF_URL,
                                        default=user_input.get(CONF_URL,
                                                               ""))] = str

            return self.async_show_form(
                step_id="user",
                data_schema=vol.Schema({
                    **url_schema,
                    vol.Optional(
                        CONF_ADMIN_USERNAME,
                        default=user_input.get(CONF_ADMIN_USERNAME),
                    ):
                    str,
                    vol.Optional(
                        CONF_ADMIN_PASSWORD,
                        default=user_input.get(CONF_ADMIN_PASSWORD),
                    ):
                    str,
                    vol.Optional(
                        CONF_SURVEILLANCE_USERNAME,
                        default=user_input.get(CONF_SURVEILLANCE_USERNAME),
                    ):
                    str,
                    vol.Optional(
                        CONF_SURVEILLANCE_PASSWORD,
                        default=user_input.get(CONF_SURVEILLANCE_PASSWORD),
                    ):
                    str,
                }),
                errors=errors,
            )

        reauth_entry = None
        if self.context.get("entry_id"):
            reauth_entry = self.hass.config_entries.async_get_entry(
                self.context["entry_id"])

        if user_input is None:
            return _get_form(
                cast(dict[str,
                          Any], reauth_entry.data) if reauth_entry else {})

        if self._hassio_discovery:
            # In case of Supervisor discovery, use pushed URL
            user_input[CONF_URL] = self._hassio_discovery[CONF_URL]

        try:
            # Cannot use cv.url validation in the schema itself, so
            # apply extra validation here.
            cv.url(user_input[CONF_URL])
        except vol.Invalid:
            return _get_form(user_input, {"base": "invalid_url"})

        client = create_motioneye_client(
            user_input[CONF_URL],
            admin_username=user_input.get(CONF_ADMIN_USERNAME),
            admin_password=user_input.get(CONF_ADMIN_PASSWORD),
            surveillance_username=user_input.get(CONF_SURVEILLANCE_USERNAME),
            surveillance_password=user_input.get(CONF_SURVEILLANCE_PASSWORD),
            session=async_get_clientsession(self.hass),
        )

        errors = {}
        try:
            await client.async_client_login()
        except MotionEyeClientConnectionError:
            errors["base"] = "cannot_connect"
        except MotionEyeClientInvalidAuthError:
            errors["base"] = "invalid_auth"
        except MotionEyeClientRequestError:
            errors["base"] = "unknown"
        finally:
            await client.async_client_close()

        if errors:
            return _get_form(user_input, errors)

        if self.context.get(
                CONF_SOURCE) == SOURCE_REAUTH and reauth_entry is not None:
            # Persist the same webhook id across reauths.
            if CONF_WEBHOOK_ID in reauth_entry.data:
                user_input[CONF_WEBHOOK_ID] = reauth_entry.data[
                    CONF_WEBHOOK_ID]
            self.hass.config_entries.async_update_entry(reauth_entry,
                                                        data=user_input)
            # Need to manually reload, as the listener won't have been
            # installed because the initial load did not succeed (the reauth
            # flow will not be initiated if the load succeeds).
            await self.hass.config_entries.async_reload(reauth_entry.entry_id)
            return self.async_abort(reason="reauth_successful")

        # Search for duplicates: there isn't a useful unique_id, but
        # at least prevent entries with the same motionEye URL.
        self._async_abort_entries_match({CONF_URL: user_input[CONF_URL]})

        title = user_input[CONF_URL]
        if self._hassio_discovery:
            title = "Add-on"

        return self.async_create_entry(
            title=title,
            data=user_input,
        )
Ejemplo n.º 16
0
 async def async_play_media(self, media_type, media_id, **kwargs):
     # Type channel
     if media_type == MEDIA_TYPE_CHANNEL:
         """Support changing a channel."""
         _LOGGER.debug("Trying to change %s to %s",media_type,media_id) 
         try:
             cv.positive_int(media_id)
         except vol.Invalid:
             _LOGGER.error("Media ID must be positive integer")
             return
         if self._api_key and self._device_id:
             if self._running_app == 'TV/HDMI' and self._cloud_source in ["DigitalTv", "digitalTv", "TV"]:
                 #In TV mode, change channel
                 if self._cloud_channel != media_id:
                     await self.hass.async_add_job(self._smartthings_keys, f"ST_CH{media_id}")
             else:
                 #Change to TV source before changing channel
                 self.hass.async_add_job(self._smartthings_keys, "ST_TV")
                 time.sleep(5)
                 smartthings.device_update(self)
                 if self._cloud_channel != media_id:
                     await self.hass.async_add_job(self._smartthings_keys, f"ST_CH{media_id}")
         else:
             keychain = ""
             for digit in media_id:
                 keychain += "KEY_{}+".format(digit)
             keychain += "KEY_ENTER"
             if self._running_app == 'TV/HDMI':
                 self.hass.async_add_job(self.async_play_media, MEDIA_TYPE_KEY, keychain)
             else:
                 found_source = False
                 for source in self._source_list:
                     if source.lower() in ["tv", "live tv", "livetv"]:
                         found_source = True
                         await self.hass.async_add_job(self.async_select_source, source)
                         time.sleep(2)
                         break
                 if found_source == False:
                     keychain = "KEY_EXIT+KEY_EXIT+{}".format(keychain)
                 self.hass.async_add_job(self.async_play_media, MEDIA_TYPE_KEY, keychain)
     # Launch an app
     elif media_type == MEDIA_TYPE_APP:
         await self.hass.async_add_job(self.send_command, media_id, "run_app")
     # Send custom key
     elif media_type == MEDIA_TYPE_KEY:
         try:
             cv.string(media_id)
         except vol.Invalid:
             _LOGGER.error('Media ID must be a string (ex: "KEY_HOME"')
             return
         source_key = media_id
         if "+" in source_key:
             all_source_keys = source_key.split("+")
             last_was_delay = True
             for this_key in all_source_keys:
                 if this_key.isdigit():
                     last_was_delay = True
                     time.sleep(int(this_key)/1000)
                 else:
                     if this_key.startswith("ST_"):
                         await self.hass.async_add_job(self._smartthings_keys, this_key)
                     else:
                         if last_was_delay == False:
                             time.sleep(DEFAULT_KEY_CHAIN_DELAY)
                         last_was_delay = False
                         self.hass.async_add_job(self.send_command, this_key)
         elif source_key.startswith("ST_"):
             await self.hass.async_add_job(self._smartthings_keys, source_key)
         else:
             await self.hass.async_add_job(self.send_command, source_key)
     # Play media
     elif media_type == MEDIA_TYPE_URL:
         try:
             cv.url(media_id)
         except vol.Invalid:
             _LOGGER.error('Media ID must be an url (ex: "http://"')
             return
         await self.hass.async_add_job(self._upnp.set_current_media, media_id)
         self._playing = True
     # Trying to make stream component work on TV
     elif media_type == "application/vnd.apple.mpegurl":
         await self.hass.async_add_job(self._upnp.set_current_media, media_id)
         self._playing = True
     elif media_type == MEDIA_TYPE_BROWSER:
         try:
             await self.hass.async_add_job(self._ws.open_browser, media_id)
         except (ConnectionResetError, AttributeError, BrokenPipeError,websocket._exceptions.WebSocketTimeoutException):
             self._ws.close()
     else:
         _LOGGER.error("Unsupported media type")
         return
Ejemplo n.º 17
0
    async def async_step_user(self,
                              user_input: dict[str, Any] | None = None
                              ) -> dict[str, Any]:
        """Handle the initial step."""
        def _get_form(user_input: dict[str, Any],
                      errors: dict[str, str] | None = None) -> dict[str, Any]:
            """Show the form to the user."""
            return self.async_show_form(  # type: ignore[no-any-return]
                step_id="user",
                data_schema=vol.Schema({
                    vol.Required(CONF_URL,
                                 default=user_input.get(CONF_URL, "")):
                    str,
                    vol.Optional(
                        CONF_ADMIN_USERNAME,
                        default=user_input.get(CONF_ADMIN_USERNAME),
                    ):
                    str,
                    vol.Optional(
                        CONF_ADMIN_PASSWORD,
                        default=user_input.get(CONF_ADMIN_PASSWORD),
                    ):
                    str,
                    vol.Optional(
                        CONF_SURVEILLANCE_USERNAME,
                        default=user_input.get(CONF_SURVEILLANCE_USERNAME),
                    ):
                    str,
                    vol.Optional(
                        CONF_SURVEILLANCE_PASSWORD,
                        default=user_input.get(CONF_SURVEILLANCE_PASSWORD),
                    ):
                    str,
                }),
                errors=errors,
            )

        reauth_entry = None
        if self.context.get("entry_id"):
            reauth_entry = self.hass.config_entries.async_get_entry(
                self.context["entry_id"])

        if user_input is None:
            return _get_form(
                cast(Dict[str,
                          Any], reauth_entry.data) if reauth_entry else {})

        try:
            # Cannot use cv.url validation in the schema itself, so
            # apply extra validation here.
            cv.url(user_input[CONF_URL])
        except vol.Invalid:
            return _get_form(user_input, {"base": "invalid_url"})

        client = create_motioneye_client(
            user_input[CONF_URL],
            admin_username=user_input.get(CONF_ADMIN_USERNAME),
            admin_password=user_input.get(CONF_ADMIN_PASSWORD),
            surveillance_username=user_input.get(CONF_SURVEILLANCE_USERNAME),
            surveillance_password=user_input.get(CONF_SURVEILLANCE_PASSWORD),
        )

        errors = {}
        try:
            await client.async_client_login()
        except MotionEyeClientConnectionError:
            errors["base"] = "cannot_connect"
        except MotionEyeClientInvalidAuthError:
            errors["base"] = "invalid_auth"
        except MotionEyeClientRequestError:
            errors["base"] = "unknown"
        finally:
            await client.async_client_close()

        if errors:
            return _get_form(user_input, errors)

        if self.context.get(
                CONF_SOURCE) == SOURCE_REAUTH and reauth_entry is not None:
            self.hass.config_entries.async_update_entry(reauth_entry,
                                                        data=user_input)
            # Need to manually reload, as the listener won't have been
            # installed because the initial load did not succeed (the reauth
            # flow will not be initiated if the load succeeds).
            await self.hass.config_entries.async_reload(reauth_entry.entry_id)
            return self.async_abort(
                reason="reauth_successful")  # type: ignore[no-any-return]

        # Search for duplicates: there isn't a useful unique_id, but
        # at least prevent entries with the same motionEye URL.
        for existing_entry in self._async_current_entries(
                include_ignore=False):
            if existing_entry.data.get(CONF_URL) == user_input[CONF_URL]:
                return self.async_abort(
                    reason="already_configured")  # type: ignore[no-any-return]

        return self.async_create_entry(  # type: ignore[no-any-return]
            title=f"{user_input[CONF_URL]}",
            data=user_input,
        )