def test_dst_does_not_exist(self, tmp_path): text = "A files are unique.\n" dst = tmp_path / "file-a.txt" helpers.write_file_if_changed(dst, text) assert dst.read_text() == text
def migrate_src_version_0_to_1(): main_cpp = CORE.relative_build_path('src', 'main.cpp') if not os.path.isfile(main_cpp): return content = read_file(main_cpp) if CPP_INCLUDE_BEGIN in content: return content, count = replace_file_content(content, r'\s*delay\((?:16|20)\);', '') if count != 0: _LOGGER.info("Migration: Removed %s occurrence of 'delay(16);' in %s", count, main_cpp) content, count = replace_file_content(content, r'using namespace esphomelib;', '') if count != 0: _LOGGER.info( "Migration: Removed %s occurrence of 'using namespace esphomelib;' " "in %s", count, main_cpp) if CPP_INCLUDE_BEGIN not in content: content, count = replace_file_content( content, r'#include "esphomelib/application.h"', CPP_INCLUDE_BEGIN + u'\n' + CPP_INCLUDE_END) if count == 0: _LOGGER.error( "Migration failed. ESPHome 1.10.0 needs to have a new auto-generated " "include section in the %s file. Please remove %s and let it be " "auto-generated again.", main_cpp, main_cpp) _LOGGER.info("Migration: Added include section to %s", main_cpp) write_file_if_changed(content, main_cpp)
def write_file(name, obj): full_path = os.path.join(args.output_path, name + ".json") if JSON_DUMP_PRETTY: json_str = json.dumps(obj, indent=2) else: json_str = json.dumps(obj, separators=(",", ":")) write_file_if_changed(full_path, json_str) print(f"Wrote {full_path}")
def test_src_and_dst_do_not_match(self, tmp_path): text = "A files are unique.\n" initial = "B files are unique.\n" dst = tmp_path / "file-a.txt" dst.write_text(initial) helpers.write_file_if_changed(dst, text) assert dst.read_text() == text
def get_ini_content(): overrides = CORE.config[CONF_ESPHOME].get(CONF_PLATFORMIO_OPTIONS, {}) lib_deps = gather_lib_deps() build_flags = gather_build_flags(overrides.pop("build_flags", [])) data = { "platform": CORE.arduino_version, "board": CORE.board, "framework": "arduino", "lib_deps": lib_deps + ["${common.lib_deps}"], "build_flags": build_flags + ["${common.build_flags}"], "upload_speed": UPLOAD_SPEED_OVERRIDE.get(CORE.board, 115200), } if CORE.is_esp32: data["board_build.partitions"] = "partitions.csv" partitions_csv = CORE.relative_build_path("partitions.csv") write_file_if_changed(partitions_csv, ESP32_LARGE_PARTITIONS_CSV) # pylint: disable=unsubscriptable-object if CONF_BOARD_FLASH_MODE in CORE.config[CONF_ESPHOME]: flash_mode = CORE.config[CONF_ESPHOME][CONF_BOARD_FLASH_MODE] data["board_build.flash_mode"] = flash_mode # Build flags if CORE.is_esp8266 and CORE.board in ESP8266_FLASH_SIZES: flash_size = ESP8266_FLASH_SIZES[CORE.board] ld_scripts = ESP8266_LD_SCRIPTS[flash_size] versions_with_old_ldscripts = [ ARDUINO_VERSION_ESP8266["2.4.0"], ARDUINO_VERSION_ESP8266["2.4.1"], ARDUINO_VERSION_ESP8266["2.4.2"], ] if CORE.arduino_version == ARDUINO_VERSION_ESP8266["2.3.0"]: # No ld script support ld_script = None if CORE.arduino_version in versions_with_old_ldscripts: # Old ld script path ld_script = ld_scripts[0] else: ld_script = ld_scripts[1] if ld_script is not None: data["board_build.ldscript"] = ld_script # Ignore libraries that are not explicitly used, but may # be added by LDF # data['lib_ldf_mode'] = 'chain' data.update(overrides) content = f"[env:{CORE.name}]\n" content += format_ini(data) return content
def get_ini_content(): lib_deps = gather_lib_deps() build_flags = gather_build_flags() data = { 'platform': CORE.arduino_version, 'board': CORE.board, 'framework': 'arduino', 'lib_deps': lib_deps + ['${common.lib_deps}'], 'build_flags': build_flags + ['${common.build_flags}'], 'upload_speed': UPLOAD_SPEED_OVERRIDE.get(CORE.board, 115200), } if CORE.is_esp32: data['board_build.partitions'] = "partitions.csv" partitions_csv = CORE.relative_build_path('partitions.csv') write_file_if_changed(partitions_csv, ESP32_LARGE_PARTITIONS_CSV) # pylint: disable=unsubscriptable-object if CONF_BOARD_FLASH_MODE in CORE.config[CONF_ESPHOME]: flash_mode = CORE.config[CONF_ESPHOME][CONF_BOARD_FLASH_MODE] data['board_build.flash_mode'] = flash_mode # Build flags if CORE.is_esp8266 and CORE.board in ESP8266_FLASH_SIZES: flash_size = ESP8266_FLASH_SIZES[CORE.board] ld_scripts = ESP8266_LD_SCRIPTS[flash_size] versions_with_old_ldscripts = [ ARDUINO_VERSION_ESP8266['2.4.0'], ARDUINO_VERSION_ESP8266['2.4.1'], ARDUINO_VERSION_ESP8266['2.4.2'], ] if CORE.arduino_version == ARDUINO_VERSION_ESP8266['2.3.0']: # No ld script support ld_script = None if CORE.arduino_version in versions_with_old_ldscripts: # Old ld script path ld_script = ld_scripts[0] else: ld_script = ld_scripts[1] if ld_script is not None: data['board_build.ldscript'] = ld_script # Ignore libraries that are not explicitly used, but may # be added by LDF # data['lib_ldf_mode'] = 'chain' data.update(CORE.config[CONF_ESPHOME].get(CONF_PLATFORMIO_OPTIONS, {})) content = f'[env:{CORE.name}]\n' content += format_ini(data) return content
def write_platformio_ini(content): update_storage_json() path = CORE.relative_build_path('platformio.ini') if os.path.isfile(path): text = read_file(path) content_format = find_begin_end(text, INI_AUTO_GENERATE_BEGIN, INI_AUTO_GENERATE_END) else: content_format = INI_BASE_FORMAT full_file = content_format[0] + INI_AUTO_GENERATE_BEGIN + '\n' + content full_file += INI_AUTO_GENERATE_END + content_format[1] write_file_if_changed(full_file, path)
def copy_files(): if CORE.using_arduino: write_file_if_changed( CORE.relative_build_path("partitions.csv"), ARDUINO_PARTITIONS_CSV, ) if CORE.using_esp_idf: _write_sdkconfig() write_file_if_changed( CORE.relative_build_path("partitions.csv"), IDF_PARTITIONS_CSV, )
def copy_src_tree(): import filecmp import shutil source_files = {} for _, component, _ in iter_components(CORE.config): source_files.update(component.source_files) # Convert to list and sort source_files_l = [it for it in source_files.items()] source_files_l.sort() # Build #include list for esphome.h include_l = [] for target, path in source_files_l: if os.path.splitext(path)[1] in HEADER_FILE_EXTENSIONS: include_l.append(u'#include "{}"'.format(target)) include_l.append(u'') include_s = u'\n'.join(include_l) source_files_copy = source_files.copy() source_files_copy.pop(DEFINES_H_TARGET) for path in walk_files(CORE.relative_src_path('esphome')): if os.path.splitext(path)[1] not in SOURCE_FILE_EXTENSIONS: # Not a source file, ignore continue # Transform path to target path name target = os.path.relpath(path, CORE.relative_src_path()).replace( os.path.sep, '/') if target == DEFINES_H_TARGET: # Ignore defines.h, will be dealt with later continue if target not in source_files_copy: # Source file removed, delete target os.remove(path) else: src_path = source_files_copy.pop(target) if not filecmp.cmp(path, src_path): # Files are not same, copy shutil.copy(src_path, path) # Now copy new files for target, src_path in source_files_copy.items(): dst_path = CORE.relative_src_path(*target.split('/')) mkdir_p(os.path.dirname(dst_path)) shutil.copy(src_path, dst_path) # Finally copy defines write_file_if_changed( generate_defines_h(), CORE.relative_src_path('esphome', 'core', 'defines.h')) write_file_if_changed(ESPHOME_README_TXT, CORE.relative_src_path('esphome', 'README.txt')) write_file_if_changed(ESPHOME_H_FORMAT.format(include_s), CORE.relative_src_path('esphome.h')) write_file_if_changed( VERSION_H_FORMAT.format(__version__), CORE.relative_src_path('esphome' 'core', 'version.h'))
def _write_sdkconfig(): # sdkconfig.{name} stores the real sdkconfig (modified by esp-idf with default) # sdkconfig.{name}.esphomeinternal stores what esphome last wrote # we use the internal one to detect if there were any changes, and if so write them to the # real sdkconfig sdk_path = Path(CORE.relative_build_path(f"sdkconfig.{CORE.name}")) internal_path = Path( CORE.relative_build_path(f"sdkconfig.{CORE.name}.esphomeinternal")) want_opts = CORE.data[KEY_ESP32][KEY_SDKCONFIG_OPTIONS] contents = ("\n".join(f"{name}={_format_sdkconfig_val(value)}" for name, value in sorted(want_opts.items())) + "\n") if write_file_if_changed(internal_path, contents): # internal changed, update real one write_file_if_changed(sdk_path, contents)
def copy_src_tree(): source_files: Dict[Path, loader.SourceFile] = CORE.extra_source_files for _, component, _ in iter_components(CORE.config): source_files.update(component.source_files) # Convert to list and sort source_files_l = list(source_files.items()) source_files_l.sort() # Build #include list for esphome.h include_l = [] for target, _ in source_files_l: if target.suffix in HEADER_FILE_EXTENSIONS: include_l.append(f'#include "{target}"') include_l.append("") include_s = "\n".join(include_l) source_files_copy = source_files.copy() ignore_targets = [Path(x) for x in (DEFINES_H_TARGET, VERSION_H_TARGET)] for t in ignore_targets: source_files_copy.pop(t) for fname in walk_files(CORE.relative_src_path("esphome")): p = Path(fname) if p.suffix not in SOURCE_FILE_EXTENSIONS: # Not a source file, ignore continue # Transform path to target path name target = p.relative_to(CORE.relative_src_path()) if target in ignore_targets: # Ignore defines.h, will be dealt with later continue if target not in source_files_copy: # Source file removed, delete target p.unlink() else: src_file = source_files_copy.pop(target) with src_file.path() as src_path: copy_file_if_changed(src_path, p) # Now copy new files for target, src_file in source_files_copy.items(): dst_path = CORE.relative_src_path(*target.parts) with src_file.path() as src_path: copy_file_if_changed(src_path, dst_path) # Finally copy defines write_file_if_changed( CORE.relative_src_path("esphome", "core", "defines.h"), generate_defines_h()) write_file_if_changed(CORE.relative_src_path("esphome", "README.txt"), ESPHOME_README_TXT) write_file_if_changed(CORE.relative_src_path("esphome.h"), ESPHOME_H_FORMAT.format(include_s)) write_file_if_changed( CORE.relative_src_path("esphome", "core", "version.h"), VERSION_H_FORMAT.format(__version__), )
def copy_src_tree(): source_files = {} for _, component, _ in iter_components(CORE.config): source_files.update(component.source_files) # Convert to list and sort source_files_l = list(source_files.items()) source_files_l.sort() # Build #include list for esphome.h include_l = [] for target, path in source_files_l: if os.path.splitext(path)[1] in HEADER_FILE_EXTENSIONS: include_l.append(f'#include "{target}"') include_l.append("") include_s = "\n".join(include_l) source_files_copy = source_files.copy() source_files_copy.pop(DEFINES_H_TARGET) for path in walk_files(CORE.relative_src_path("esphome")): if os.path.splitext(path)[1] not in SOURCE_FILE_EXTENSIONS: # Not a source file, ignore continue # Transform path to target path name target = os.path.relpath(path, CORE.relative_src_path()).replace( os.path.sep, "/" ) if target in (DEFINES_H_TARGET, VERSION_H_TARGET): # Ignore defines.h, will be dealt with later continue if target not in source_files_copy: # Source file removed, delete target os.remove(path) else: src_path = source_files_copy.pop(target) copy_file_if_changed(src_path, path) # Now copy new files for target, src_path in source_files_copy.items(): dst_path = CORE.relative_src_path(*target.split("/")) copy_file_if_changed(src_path, dst_path) # Finally copy defines write_file_if_changed( CORE.relative_src_path("esphome", "core", "defines.h"), generate_defines_h() ) write_file_if_changed( CORE.relative_src_path("esphome", "README.txt"), ESPHOME_README_TXT ) write_file_if_changed( CORE.relative_src_path("esphome.h"), ESPHOME_H_FORMAT.format(include_s) ) write_file_if_changed( CORE.relative_src_path("esphome", "core", "version.h"), VERSION_H_FORMAT.format(__version__), )
def write_cpp(code_s): path = CORE.relative_src_path('main.cpp') if os.path.isfile(path): text = read_file(path) code_format = find_begin_end(text, CPP_AUTO_GENERATE_BEGIN, CPP_AUTO_GENERATE_END) code_format_ = find_begin_end(code_format[0], CPP_INCLUDE_BEGIN, CPP_INCLUDE_END) code_format = (code_format_[0], code_format_[1], code_format[1]) else: code_format = CPP_BASE_FORMAT copy_src_tree() global_s = u'#include "esphome.h"\n' global_s += CORE.cpp_global_section full_file = code_format[0] + CPP_INCLUDE_BEGIN + u'\n' + global_s + CPP_INCLUDE_END full_file += code_format[1] + CPP_AUTO_GENERATE_BEGIN + u'\n' + code_s + CPP_AUTO_GENERATE_END full_file += code_format[2] write_file_if_changed(full_file, path)
def copy_files(): if CORE.using_arduino: write_file_if_changed( CORE.relative_build_path("partitions.csv"), ARDUINO_PARTITIONS_CSV, ) if CORE.using_esp_idf: _write_sdkconfig() write_file_if_changed( CORE.relative_build_path("partitions.csv"), IDF_PARTITIONS_CSV, ) dir = os.path.dirname(__file__) post_build_file = os.path.join(dir, "post_build.py.script") copy_file_if_changed( post_build_file, CORE.relative_build_path("post_build.py"), )
def copy_files(): if CORE.using_arduino: write_file_if_changed( CORE.relative_build_path("partitions.csv"), ARDUINO_PARTITIONS_CSV, ) if CORE.using_esp_idf: _write_sdkconfig() write_file_if_changed( CORE.relative_build_path("partitions.csv"), IDF_PARTITIONS_CSV, ) # IDF build scripts look for version string to put in the build. # However, if the build path does not have an initialized git repo, # and no version.txt file exists, the CMake script fails for some setups. # Fix by manually pasting a version.txt file, containing the ESPHome version write_file_if_changed( CORE.relative_build_path("version.txt"), __version__, ) dir = os.path.dirname(__file__) post_build_file = os.path.join(dir, "post_build.py.script") copy_file_if_changed( post_build_file, CORE.relative_build_path("post_build.py"), )
def dump_schema(): import esphome.config_validation as cv from esphome import automation from esphome.automation import validate_potentially_and_condition from esphome import pins from esphome.core import CORE from esphome.helpers import write_file_if_changed from esphome.components import remote_base # The root directory of the repo root = Path(__file__).parent.parent # Fake some directory so that get_component works CORE.config_path = str(root) file_path = args.output schema_registry[cv.boolean] = {"type": "boolean"} for v in [ cv.int_, cv.int_range, cv.positive_int, cv.float_, cv.positive_float, cv.positive_float, cv.positive_not_null_int, cv.negative_one_to_one_float, cv.port, ]: schema_registry[v] = {"type": "number"} for v in [ cv.string, cv.string_strict, cv.valid_name, cv.hex_int, cv.hex_int_range, pins.output_pin, pins.input_pin, pins.input_pullup_pin, cv.float_with_unit, cv.subscribe_topic, cv.publish_topic, cv.mqtt_payload, cv.ssid, cv.percentage_int, cv.percentage, cv.possibly_negative_percentage, cv.positive_time_period, cv.positive_time_period_microseconds, cv.positive_time_period_milliseconds, cv.positive_time_period_minutes, cv.positive_time_period_seconds, ]: schema_registry[v] = {"type": "string"} schema_registry[validate_potentially_and_condition] = get_ref("condition_list") for v in [pins.gpio_input_pin_schema, pins.gpio_input_pullup_pin_schema]: schema_registry[v] = get_ref("PIN.GPIO_FULL_INPUT_PIN_SCHEMA") for v in [pins.internal_gpio_input_pin_schema, pins.input_pin]: schema_registry[v] = get_ref("PIN.INPUT_INTERNAL") for v in [pins.gpio_output_pin_schema, pins.internal_gpio_output_pin_schema]: schema_registry[v] = get_ref("PIN.GPIO_FULL_OUTPUT_PIN_SCHEMA") for v in [pins.internal_gpio_output_pin_schema, pins.output_pin]: schema_registry[v] = get_ref("PIN.OUTPUT_INTERNAL") add_module_schemas("CONFIG", cv) get_jschema("POLLING_COMPONENT", cv.polling_component_schema("60s")) add_pin_schema() add_module_schemas("REMOTE_BASE", remote_base) add_module_schemas("AUTOMATION", automation) load_components() add_registries() definitions["condition_list"] = { JSC_ONEOF: [ {"type": "array", "items": get_ref(JSC_CONDITION)}, get_ref(JSC_CONDITION), ] } output = { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "definitions": definitions, JSC_PROPERTIES: base_props, } add_core() add_buses() add_components() add_registries() # need second pass, e.g. climate.pid.autotune add_pin_registry() solve_pending_refs() write_file_if_changed(file_path, json.dumps(output)) print(f"Wrote {file_path}")
if not platform_path.is_file() or platform_path.name == '__init__.py': continue codeowners[f'esphome/components/{name}/*'].extend(platform.codeowners) for path, owners in sorted(codeowners.items()): owners = sorted(set(owners)) if not owners: continue for owner in owners: if not owner.startswith('@'): print( f"Codeowner {owner} for integration {path} must start with an '@' symbol!" ) sys.exit(1) parts.append(f"{path} {' '.join(owners)}") # End newline parts.append('') content = '\n'.join(parts) codeowners_file = root / 'CODEOWNERS' if args.check: if codeowners_file.read_text() != content: print("CODEOWNERS file is not up to date.") print("Please run `script/build_codeowners.py`") sys.exit(1) print("CODEOWNERS file is up to date") else: write_file_if_changed(codeowners_file, content) print("Wrote CODEOWNERS")
def save(self, path): write_file_if_changed(path, self.to_json())
def save(self, path): # type: (str) -> None write_file_if_changed(path, self.to_json())