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 ), )
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, )
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
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, )
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
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}))
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)
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:
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)
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}
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
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
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, )
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, )
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
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, )