mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-04 00:51:49 +00:00 
			
		
		
		
	Compare commits
	
		
			2 Commits
		
	
	
		
			2024.9.0b1
			...
			jesserockz
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					ab8ac8707b | ||
| 
						 | 
					47c68c8aef | 
@@ -139,6 +139,7 @@ esphome/components/ezo_pmp/* @carlos-sarmiento
 | 
				
			|||||||
esphome/components/factory_reset/* @anatoly-savchenkov
 | 
					esphome/components/factory_reset/* @anatoly-savchenkov
 | 
				
			||||||
esphome/components/fastled_base/* @OttoWinter
 | 
					esphome/components/fastled_base/* @OttoWinter
 | 
				
			||||||
esphome/components/feedback/* @ianchi
 | 
					esphome/components/feedback/* @ianchi
 | 
				
			||||||
 | 
					esphome/components/file/* @jesserockz
 | 
				
			||||||
esphome/components/fingerprint_grow/* @OnFreund @alexborro @loongyh
 | 
					esphome/components/fingerprint_grow/* @OnFreund @alexborro @loongyh
 | 
				
			||||||
esphome/components/font/* @clydebarrow @esphome/core
 | 
					esphome/components/font/* @clydebarrow @esphome/core
 | 
				
			||||||
esphome/components/fs3000/* @kahrendt
 | 
					esphome/components/fs3000/* @kahrendt
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										148
									
								
								esphome/components/file/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								esphome/components/file/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,148 @@
 | 
				
			|||||||
 | 
					import hashlib
 | 
				
			||||||
 | 
					import logging
 | 
				
			||||||
 | 
					from pathlib import Path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from magic import Magic
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from esphome import external_files
 | 
				
			||||||
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
 | 
					from esphome.const import (
 | 
				
			||||||
 | 
					    CONF_FILE,
 | 
				
			||||||
 | 
					    CONF_FORMAT,
 | 
				
			||||||
 | 
					    CONF_ID,
 | 
				
			||||||
 | 
					    CONF_PATH,
 | 
				
			||||||
 | 
					    CONF_TYPE,
 | 
				
			||||||
 | 
					    CONF_URL,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from esphome.core import CORE, HexInt
 | 
				
			||||||
 | 
					from esphome.external_files import download_content
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_LOGGER = logging.getLogger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CODEOWNERS = ["@jesserockz"]
 | 
				
			||||||
 | 
					DOMAIN = "file"
 | 
				
			||||||
 | 
					MULTI_CONF = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TYPE_LOCAL = "local"
 | 
				
			||||||
 | 
					TYPE_WEB = "web"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FORMAT_RAW = "raw"
 | 
				
			||||||
 | 
					FORMAT_WAV = "wav"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FORMATS = [FORMAT_RAW, FORMAT_WAV]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _compute_local_file_path(value: dict) -> Path:
 | 
				
			||||||
 | 
					    url = value[CONF_URL]
 | 
				
			||||||
 | 
					    h = hashlib.new("sha256")
 | 
				
			||||||
 | 
					    h.update(url.encode())
 | 
				
			||||||
 | 
					    key = h.hexdigest()[:8]
 | 
				
			||||||
 | 
					    base_dir = external_files.compute_local_file_dir(DOMAIN)
 | 
				
			||||||
 | 
					    _LOGGER.debug("_compute_local_file_path: base_dir=%s", base_dir / key)
 | 
				
			||||||
 | 
					    return base_dir / key
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _download_web_file(value: dict) -> dict:
 | 
				
			||||||
 | 
					    url = value[CONF_URL]
 | 
				
			||||||
 | 
					    path = _compute_local_file_path(value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    download_content(url, path)
 | 
				
			||||||
 | 
					    _LOGGER.debug("download_web_file: path=%s", path)
 | 
				
			||||||
 | 
					    return value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LOCAL_SCHEMA = cv.Schema(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        cv.Required(CONF_PATH): cv.file_,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					WEB_SCHEMA = cv.All(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        cv.Required(CONF_URL): cv.url,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    _download_web_file,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _validate_file_shorthand(value):
 | 
				
			||||||
 | 
					    value = cv.string_strict(value)
 | 
				
			||||||
 | 
					    if value.startswith("http://") or value.startswith("https://"):
 | 
				
			||||||
 | 
					        return _file_schema(
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                CONF_TYPE: TYPE_WEB,
 | 
				
			||||||
 | 
					                CONF_URL: value,
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    return _file_schema(
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            CONF_TYPE: TYPE_LOCAL,
 | 
				
			||||||
 | 
					            CONF_PATH: value,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TYPED_FILE_SCHEMA = cv.typed_schema(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        TYPE_LOCAL: LOCAL_SCHEMA,
 | 
				
			||||||
 | 
					        TYPE_WEB: WEB_SCHEMA,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _file_schema(value):
 | 
				
			||||||
 | 
					    if isinstance(value, str):
 | 
				
			||||||
 | 
					        return _validate_file_shorthand(value)
 | 
				
			||||||
 | 
					    return TYPED_FILE_SCHEMA(value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONFIG_SCHEMA = cv.Schema(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        cv.Required(CONF_ID): cv.declare_id(cg.uint8),
 | 
				
			||||||
 | 
					        cv.Required(CONF_FILE): _file_schema,
 | 
				
			||||||
 | 
					        cv.Optional(CONF_FORMAT): cv.one_of(*FORMATS, lower=True),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _trim_wav_file(data: bytes) -> bytes:
 | 
				
			||||||
 | 
					    header = []
 | 
				
			||||||
 | 
					    index = 0
 | 
				
			||||||
 | 
					    length = len(data)
 | 
				
			||||||
 | 
					    while index < length:
 | 
				
			||||||
 | 
					        byte = data[index : index + 1]
 | 
				
			||||||
 | 
					        if byte == b"":
 | 
				
			||||||
 | 
					            raise ValueError("Could not find data in wav file")
 | 
				
			||||||
 | 
					        header.append(byte)
 | 
				
			||||||
 | 
					        index += 1
 | 
				
			||||||
 | 
					        if header[-4:] == [b"d", b"a", b"t", b"a"] or index > 100:
 | 
				
			||||||
 | 
					            break
 | 
				
			||||||
 | 
					    index += 2
 | 
				
			||||||
 | 
					    return data[index:]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def to_code(config: dict) -> None:
 | 
				
			||||||
 | 
					    conf_file: dict = config[CONF_FILE]
 | 
				
			||||||
 | 
					    file_source = conf_file[CONF_TYPE]
 | 
				
			||||||
 | 
					    if file_source == TYPE_LOCAL:
 | 
				
			||||||
 | 
					        path = CORE.relative_config_path(conf_file[CONF_PATH])
 | 
				
			||||||
 | 
					    elif file_source == TYPE_WEB:
 | 
				
			||||||
 | 
					        path = _compute_local_file_path(conf_file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    with open(path, "rb") as f:
 | 
				
			||||||
 | 
					        data = f.read()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Get format from config or fallback to magic
 | 
				
			||||||
 | 
					    if (format := config.get(CONF_FORMAT)) is None:
 | 
				
			||||||
 | 
					        magic = Magic(mime=True)
 | 
				
			||||||
 | 
					        file_type = magic.from_buffer(data)
 | 
				
			||||||
 | 
					        if "wav" in file_type:
 | 
				
			||||||
 | 
					            format = FORMAT_WAV
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if format == FORMAT_WAV:
 | 
				
			||||||
 | 
					        data = _trim_wav_file(data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    rhs = [HexInt(x) for x in data]
 | 
				
			||||||
 | 
					    cg.progmem_array(config[CONF_ID], rhs)
 | 
				
			||||||
@@ -58,7 +58,7 @@ file_types = (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
cpp_include = ("*.h", "*.c", "*.cpp", "*.tcc")
 | 
					cpp_include = ("*.h", "*.c", "*.cpp", "*.tcc")
 | 
				
			||||||
py_include = ("*.py",)
 | 
					py_include = ("*.py",)
 | 
				
			||||||
ignore_types = (".ico", ".png", ".woff", ".woff2", "", ".ttf", ".otf")
 | 
					ignore_types = (".ico", ".png", ".woff", ".woff2", "", ".ttf", ".otf", ".wav")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LINT_FILE_CHECKS = []
 | 
					LINT_FILE_CHECKS = []
 | 
				
			||||||
LINT_CONTENT_CHECKS = []
 | 
					LINT_CONTENT_CHECKS = []
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								tests/components/file/bloop.wav
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								tests/components/file/bloop.wav
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										7
									
								
								tests/components/file/test.esp32-idf.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								tests/components/file/test.esp32-idf.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					file:
 | 
				
			||||||
 | 
					  - id: bloop_local
 | 
				
			||||||
 | 
					    file: ../../components/file/bloop.wav
 | 
				
			||||||
 | 
					  - id: bloop_web
 | 
				
			||||||
 | 
					    # TODO: Change to ESPHome URL after file is merged
 | 
				
			||||||
 | 
					    # file: https://github.com/esphome/esphome/raw/dev/tests/components/file/bloop.wav
 | 
				
			||||||
 | 
					    file: https://github.com/jesserockz/esphome-components/raw/main/tests/file/bloop.wav
 | 
				
			||||||
		Reference in New Issue
	
	Block a user