"""Generate an integration.""" from pathlib import Path from .model import Info TEMPLATE_DIR = Path(__file__).parent / "templates" TEMPLATE_INTEGRATION = TEMPLATE_DIR / "integration" TEMPLATE_TESTS = TEMPLATE_DIR / "tests" def generate(template: str, info: Info) -> None: """Generate a template.""" print(f"Scaffolding {template} for the {info.domain} integration...") _ensure_tests_dir_exists(info) _generate(TEMPLATE_DIR / template / "integration", info.integration_dir, info) _generate(TEMPLATE_DIR / template / "tests", info.tests_dir, info) _custom_tasks(template, info) print() def _generate(src_dir, target_dir, info: Info) -> None: """Generate an integration.""" replaces = {"NEW_DOMAIN": info.domain, "NEW_NAME": info.name} if not target_dir.exists(): target_dir.mkdir() for source_file in src_dir.glob("**/*"): content = source_file.read_text() for to_search, to_replace in replaces.items(): content = content.replace(to_search, to_replace) target_file = target_dir / source_file.relative_to(src_dir) # If the target file exists, create our template as EXAMPLE_. # Exception: If we are creating a new integration, we can end up running integration base # and a config flows on top of one another. In that case, we want to override the files. if not info.is_new and target_file.exists(): new_name = f"EXAMPLE_{target_file.name}" print(f"File {target_file} already exists, creating {new_name} instead.") target_file = target_file.parent / new_name info.examples_added.add(target_file) elif src_dir.name == "integration": info.files_added.add(target_file) else: info.tests_added.add(target_file) print(f"Writing {target_file}") target_file.write_text(content) def _ensure_tests_dir_exists(info: Info) -> None: """Ensure a test dir exists.""" if info.tests_dir.exists(): return info.tests_dir.mkdir() print(f"Writing {info.tests_dir / '__init__.py'}") (info.tests_dir / "__init__.py").write_text( f'"""Tests for the {info.name} integration."""\n' ) def _append(path: Path, text): """Append some text to a path.""" path.write_text(path.read_text() + text) def _custom_tasks(template, info: Info) -> None: """Handle custom tasks for templates.""" if template == "integration": changes = {"codeowners": [info.codeowner], "iot_class": info.iot_class} if info.requirement: changes["requirements"] = [info.requirement] info.update_manifest(**changes) elif template == "device_trigger": info.update_strings( device_automation={ **info.strings().get("device_automation", {}), "trigger_type": { "turned_on": "{entity_name} turned on", "turned_off": "{entity_name} turned off", }, } ) elif template == "device_condition": info.update_strings( device_automation={ **info.strings().get("device_automation", {}), "condition_type": { "is_on": "{entity_name} is on", "is_off": "{entity_name} is off", }, } ) elif template == "device_action": info.update_strings( device_automation={ **info.strings().get("device_automation", {}), "action_type": { "turn_on": "Turn on {entity_name}", "turn_off": "Turn off {entity_name}", }, } ) elif template == "config_flow": info.update_manifest(config_flow=True) info.update_strings( config={ "step": { "user": { "data": { "host": "[%key:common::config_flow::data::host%]", "username": "[%key:common::config_flow::data::username%]", "password": "[%key:common::config_flow::data::password%]", }, } }, "error": { "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]", "unknown": "[%key:common::config_flow::error::unknown%]", }, "abort": { "already_configured": "[%key:common::config_flow::abort::already_configured_device%]" }, }, ) elif template == "config_flow_discovery": info.update_manifest(config_flow=True) info.update_strings( config={ "step": { "confirm": { "description": "[%key:common::config_flow::description::confirm_setup%]", } }, "abort": { "single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]", "no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]", }, }, ) elif template == "config_flow_helper": info.update_manifest(config_flow=True, integration_type="helper") info.update_strings( config={ "step": { "user": { "description": "New NEW_NAME Sensor", "data": {"entity_id": "Input sensor", "name": "Name"}, }, }, }, options={ "step": { "init": { "data": { "entity_id": "[%key:component::NEW_DOMAIN::config::step::user::description%]" }, }, }, }, ) elif template == "config_flow_oauth2": info.update_manifest(config_flow=True, dependencies=["application_credentials"]) info.update_strings( config={ "step": { "pick_implementation": { "title": "[%key:common::config_flow::title::oauth2_pick_implementation%]" } }, "abort": { "already_configured": "[%key:common::config_flow::abort::already_configured_account%]", "already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]", "oauth_error": "[%key:common::config_flow::abort::oauth2_error%]", "missing_configuration": "[%key:common::config_flow::abort::oauth2_missing_configuration%]", "authorize_url_timeout": "[%key:common::config_flow::abort::oauth2_authorize_url_timeout%]", "no_url_available": "[%key:common::config_flow::abort::oauth2_no_url_available%]", "user_rejected_authorize": "[%key:common::config_flow::abort::oauth2_user_rejected_authorize%]", }, "create_entry": { "default": "[%key:common::config_flow::create_entry::authenticated%]" }, }, )