"""Gather info for scaffolding.""" import json from homeassistant.util import slugify from script.hassfest.manifest import SUPPORTED_IOT_CLASSES from .const import COMPONENT_DIR from .error import ExitApp from .model import Info CHECK_EMPTY = ["Cannot be empty", lambda value: value] def gather_info(arguments) -> Info: """Gather info.""" if arguments.integration: info = {"domain": arguments.integration} elif arguments.develop: print("Running in developer mode. Automatically filling in info.") print() info = {"domain": "develop"} else: info = _gather_info( { "domain": { "prompt": "What is the domain?", "validators": [ CHECK_EMPTY, [ "Domains cannot contain spaces or special characters.", lambda value: value == slugify(value), ], ], } } ) info["is_new"] = not (COMPONENT_DIR / info["domain"] / "manifest.json").exists() if not info["is_new"]: return _load_existing_integration(info["domain"]) if arguments.develop: info.update( { "name": "Develop Hub", "codeowner": "@developer", "requirement": "aiodevelop==1.2.3", "oauth2": True, "iot_class": "local_polling", } ) else: info.update(gather_new_integration(arguments.template == "integration")) return Info(**info) YES_NO = { "validators": [["Type either 'yes' or 'no'", lambda value: value in ("yes", "no")]], "converter": lambda value: value == "yes", } def gather_new_integration(determine_auth: bool) -> Info: """Gather info about new integration from user.""" fields = { "name": { "prompt": "What is the name of your integration?", "validators": [CHECK_EMPTY], }, "codeowner": { "prompt": "What is your GitHub handle?", "validators": [ CHECK_EMPTY, [ 'GitHub handles need to start with an "@"', lambda value: value.startswith("@"), ], ], }, "requirement": { "prompt": "What PyPI package and version do you depend on? Leave blank for none.", "validators": [ [ "Versions should be pinned using '=='.", lambda value: not value or "==" in value, ] ], }, "iot_class": { "prompt": ( f"""How will your integration gather data? Valid values are {', '.join(SUPPORTED_IOT_CLASSES)} More info @ https://developers.home-assistant.io/docs/creating_integration_manifest#iot-class """ ), "validators": [ [ f"You need to pick one of {', '.join(SUPPORTED_IOT_CLASSES)}", lambda value: value in SUPPORTED_IOT_CLASSES, ] ], }, } if determine_auth: fields.update( { "authentication": { "prompt": "Does Home Assistant need the user to authenticate to control the device/service? (yes/no)", "default": "yes", **YES_NO, }, "discoverable": { "prompt": "Is the device/service discoverable on the local network? (yes/no)", "default": "no", **YES_NO, }, "oauth2": { "prompt": "Can the user authenticate the device using OAuth2? (yes/no)", "default": "no", **YES_NO, }, } ) return _gather_info(fields) def _load_existing_integration(domain) -> Info: """Load an existing integration.""" if not (COMPONENT_DIR / domain).exists(): raise ExitApp("Integration does not exist", 1) manifest = json.loads((COMPONENT_DIR / domain / "manifest.json").read_text()) return Info(domain=domain, name=manifest["name"], is_new=False) def _gather_info(fields) -> dict: """Gather info from user.""" answers = {} for key, info in fields.items(): hint = None while key not in answers: if hint is not None: print() print(f"Error: {hint}") try: print() msg = info["prompt"] if "default" in info: msg += f" [{info['default']}]" value = input(f"{msg}\n> ") except (KeyboardInterrupt, EOFError): raise ExitApp("Interrupted!", 1) value = value.strip() if value == "" and "default" in info: value = info["default"] hint = None for validator_hint, validator in info["validators"]: if not validator(value): hint = validator_hint break if hint is None: if "converter" in info: value = info["converter"](value) answers[key] = value return answers