1 """Support for functionality to download files."""
3 from __future__
import annotations
5 from http
import HTTPStatus
11 import voluptuous
as vol
27 DOWNLOAD_COMPLETED_EVENT,
28 DOWNLOAD_FAILED_EVENT,
29 SERVICE_DOWNLOAD_FILE,
34 """Listen for download events to download files."""
35 download_path = entry.data[CONF_DOWNLOAD_DIR]
38 if not os.path.isabs(download_path):
39 download_path = hass.config.path(download_path)
41 if not await hass.async_add_executor_job(os.path.isdir, download_path):
43 "Download path %s does not exist. File Downloader not active", download_path
47 def download_file(service: ServiceCall) ->
None:
48 """Start thread to download file specified in the URL."""
50 def do_download() -> None:
51 """Download the file."""
53 url = service.data[ATTR_URL]
55 subdir = service.data.get(ATTR_SUBDIR)
57 filename = service.data.get(ATTR_FILENAME)
59 overwrite = service.data.get(ATTR_OVERWRITE)
67 req = requests.get(url, stream=
True, timeout=10)
69 if req.status_code != HTTPStatus.OK:
71 "Downloading '%s' failed, status_code=%d", url, req.status_code
74 f
"{DOMAIN}_{DOWNLOAD_FAILED_EVENT}",
75 {
"url": url,
"filename": filename},
79 if filename
is None and "content-disposition" in req.headers:
81 r"filename=(\S+)", req.headers[
"content-disposition"]
85 filename = match[0].strip(
"'\" ")
88 filename = os.path.basename(url).strip()
91 filename =
"ha_download"
98 subdir_path = os.path.join(download_path, subdir)
101 os.makedirs(subdir_path, exist_ok=
True)
103 final_path = os.path.join(subdir_path, filename)
106 final_path = os.path.join(download_path, filename)
108 path, ext = os.path.splitext(final_path)
114 final_path = path + ext
115 while os.path.isfile(final_path):
118 final_path = f
"{path}_{tries}.{ext}"
120 _LOGGER.debug(
"%s -> %s", url, final_path)
122 with open(final_path,
"wb")
as fil:
123 for chunk
in req.iter_content(1024):
126 _LOGGER.debug(
"Downloading of %s done", url)
128 f
"{DOMAIN}_{DOWNLOAD_COMPLETED_EVENT}",
129 {
"url": url,
"filename": filename},
132 except requests.exceptions.ConnectionError:
133 _LOGGER.exception(
"ConnectionError occurred for %s", url)
135 f
"{DOMAIN}_{DOWNLOAD_FAILED_EVENT}",
136 {
"url": url,
"filename": filename},
140 if final_path
and os.path.isfile(final_path):
141 os.remove(final_path)
143 _LOGGER.exception(
"Invalid value")
145 f
"{DOMAIN}_{DOWNLOAD_FAILED_EVENT}",
146 {
"url": url,
"filename": filename},
150 if final_path
and os.path.isfile(final_path):
151 os.remove(final_path)
153 threading.Thread(target=do_download).start()
158 SERVICE_DOWNLOAD_FILE,
162 vol.Optional(ATTR_FILENAME): cv.string,
163 vol.Optional(ATTR_SUBDIR): cv.string,
164 vol.Required(ATTR_URL): cv.url,
165 vol.Optional(ATTR_OVERWRITE, default=
False): cv.boolean,
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
None open(self, **Any kwargs)
None async_register_admin_service(HomeAssistant hass, str domain, str service, Callable[[ServiceCall], Awaitable[None]|None] service_func, VolSchemaType schema=vol.Schema({}, extra=vol.PREVENT_EXTRA))
None raise_if_invalid_path(str path)
None raise_if_invalid_filename(str filename)