mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-03 08:31:47 +00:00 
			
		
		
		
	Compare commits
	
		
			2 Commits
		
	
	
		
			action_cha
			...
			jesserockz
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					ab8ac8707b | ||
| 
						 | 
					47c68c8aef | 
@@ -139,6 +139,7 @@ esphome/components/ezo_pmp/* @carlos-sarmiento
 | 
			
		||||
esphome/components/factory_reset/* @anatoly-savchenkov
 | 
			
		||||
esphome/components/fastled_base/* @OttoWinter
 | 
			
		||||
esphome/components/feedback/* @ianchi
 | 
			
		||||
esphome/components/file/* @jesserockz
 | 
			
		||||
esphome/components/fingerprint_grow/* @OnFreund @alexborro @loongyh
 | 
			
		||||
esphome/components/font/* @clydebarrow @esphome/core
 | 
			
		||||
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")
 | 
			
		||||
py_include = ("*.py",)
 | 
			
		||||
ignore_types = (".ico", ".png", ".woff", ".woff2", "", ".ttf", ".otf")
 | 
			
		||||
ignore_types = (".ico", ".png", ".woff", ".woff2", "", ".ttf", ".otf", ".wav")
 | 
			
		||||
 | 
			
		||||
LINT_FILE_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