1 """Start Home Assistant."""
3 from __future__
import annotations
6 from contextlib
import suppress
12 from .backup_restore
import restore_backup
13 from .const
import REQUIRED_PYTHON_VER, RESTART_EXIT_CODE, __version__
15 FAULT_LOG_FILENAME =
"home-assistant.log.fault"
19 """Validate that Home Assistant is running in a supported operating system."""
20 if not sys.platform.startswith((
"darwin",
"linux")):
22 "Home Assistant only supports Linux, OSX and Windows using WSL",
29 """Validate that the right Python version is running."""
30 if sys.version_info[:3] < REQUIRED_PYTHON_VER:
32 "Home Assistant requires at least Python "
33 f
"{REQUIRED_PYTHON_VER[0]}.{REQUIRED_PYTHON_VER[1]}.{REQUIRED_PYTHON_VER[2]}",
40 """Validate the configuration directory."""
42 from .
import config
as config_util
44 lib_dir = os.path.join(config_dir,
"deps")
47 if not os.path.isdir(config_dir):
48 if config_dir != config_util.get_default_config_dir():
49 if os.path.exists(config_dir):
50 reason =
"is not a directory"
52 reason =
"does not exist"
54 f
"Fatal Error: Specified configuration directory {config_dir} {reason}",
63 "Fatal Error: Unable to create default configuration "
64 f
"directory {config_dir}: {ex}",
70 if not os.path.isdir(lib_dir):
75 f
"Fatal Error: Unable to create library directory {lib_dir}: {ex}",
82 """Get parsed passed in arguments."""
84 from .
import config
as config_util
86 parser = argparse.ArgumentParser(
87 description=
"Home Assistant: Observe, Control, Automate.",
88 epilog=f
"If restart is requested, exits with code {RESTART_EXIT_CODE}",
90 parser.add_argument(
"--version", action=
"version", version=__version__)
94 metavar=
"path_to_config_dir",
95 default=config_util.get_default_config_dir(),
96 help=
"Directory that contains the Home Assistant configuration",
101 help=
"Start Home Assistant in recovery mode",
104 "--debug", action=
"store_true", help=
"Start Home Assistant in debug mode"
107 "--open-ui", action=
"store_true", help=
"Open the webinterface in a browser"
110 skip_pip_group = parser.add_mutually_exclusive_group()
111 skip_pip_group.add_argument(
114 help=
"Skips pip install of required packages on startup",
116 skip_pip_group.add_argument(
117 "--skip-pip-packages",
118 metavar=
"package_names",
119 type=
lambda arg: arg.split(
","),
121 help=
"Skip pip install of specific packages on startup",
125 "-v",
"--verbose", action=
"store_true", help=
"Enable verbose logging to file."
131 help=
"Enables daily log rotation and keeps up to the specified days",
137 help=
"Log file to write to. If not set, CONFIG/home-assistant.log is used",
140 "--log-no-color", action=
"store_true", help=
"Disable color logs"
143 "--script", nargs=argparse.REMAINDER, help=
"Run one of the embedded scripts"
148 help=
"Skips validation of operating system",
151 return parser.parse_args()
155 """Check if there are any lingering threads."""
158 thread.is_alive()
and not thread.daemon
for thread
in threading.enumerate()
161 sys.stderr.write(f
"Found {nthreads} non-daemonic threads.\n")
166 except AssertionError:
167 sys.stderr.write(
"Failed to count non-daemonic threads.\n")
171 """Start Home Assistant."""
176 if not args.ignore_os_check:
179 if args.script
is not None:
181 from .
import scripts
183 return scripts.run(args.script)
185 config_dir = os.path.abspath(os.path.join(os.getcwd(), args.config))
187 return RESTART_EXIT_CODE
192 from .
import config, runner
194 safe_mode = config.safe_mode_enabled(config_dir)
197 config_dir=config_dir,
198 verbose=args.verbose,
199 log_rotate_days=args.log_rotate_days,
200 log_file=args.log_file,
201 log_no_color=args.log_no_color,
202 skip_pip=args.skip_pip,
203 skip_pip_packages=args.skip_pip_packages,
204 recovery_mode=args.recovery_mode,
206 open_ui=args.open_ui,
210 fault_file_name = os.path.join(config_dir, FAULT_LOG_FILENAME)
211 with open(fault_file_name, mode=
"a", encoding=
"utf8")
as fault_file:
212 faulthandler.enable(fault_file)
213 exit_code = runner.run(runtime_conf)
214 faulthandler.disable()
217 with suppress(FileNotFoundError):
218 if os.path.getsize(fault_file_name) == 0:
219 os.remove(fault_file_name)
226 if __name__ ==
"__main__":
None ensure_config_path(str config_dir)
argparse.Namespace get_arguments()
bool restore_backup(str config_dir_path)
None open(self, **Any kwargs)