diff --git a/homeassistant/__main__.py b/homeassistant/__main__.py index e97ed0c6386..7da1e6658e1 100644 --- a/homeassistant/__main__.py +++ b/homeassistant/__main__.py @@ -1,7 +1,10 @@ """ Starts home assistant. """ from __future__ import print_function +from multiprocessing import Process +import signal import sys +import threading import os import argparse @@ -204,6 +207,61 @@ def uninstall_osx(): print("Home Assistant has been uninstalled.") +def setup_and_run_hass(config_dir, args): + """ Setup HASS and run. Block until stopped. """ + if args.demo_mode: + config = { + 'frontend': {}, + 'demo': {} + } + hass = bootstrap.from_config_dict( + config, config_dir=config_dir, daemon=args.daemon, + verbose=args.verbose, skip_pip=args.skip_pip, + log_rotate_days=args.log_rotate_days) + else: + config_file = ensure_config_file(config_dir) + print('Config directory:', config_dir) + hass = bootstrap.from_config_file( + config_file, daemon=args.daemon, verbose=args.verbose, + skip_pip=args.skip_pip, log_rotate_days=args.log_rotate_days) + + if args.open_ui: + def open_browser(event): + """ Open the webinterface in a browser. """ + if hass.config.api is not None: + import webbrowser + webbrowser.open(hass.config.api.base_url) + + hass.bus.listen_once(EVENT_HOMEASSISTANT_START, open_browser) + + hass.start() + sys.exit(int(hass.block_till_stopped())) + + +def run_hass_process(hass_proc): + """ Runs a child hass process. Returns True if it should be restarted. """ + requested_stop = threading.Event() + hass_proc.daemon = True + + def request_stop(): + """ request hass stop """ + requested_stop.set() + hass_proc.terminate() + + try: + signal.signal(signal.SIGTERM, request_stop) + except ValueError: + print('Could not bind to SIGQUIT. Are you running in a thread?') + + hass_proc.start() + try: + hass_proc.join() + except KeyboardInterrupt: + request_stop() + hass_proc.join() + return not requested_stop.isSet() and hass_proc.exitcode > 0 + + def main(): """ Starts Home Assistant. """ validate_python() @@ -233,33 +291,12 @@ def main(): if args.pid_file: write_pid(args.pid_file) - if args.demo_mode: - config = { - 'frontend': {}, - 'demo': {} - } - hass = bootstrap.from_config_dict( - config, config_dir=config_dir, daemon=args.daemon, - verbose=args.verbose, skip_pip=args.skip_pip, - log_rotate_days=args.log_rotate_days) - else: - config_file = ensure_config_file(config_dir) - print('Config directory:', config_dir) - hass = bootstrap.from_config_file( - config_file, daemon=args.daemon, verbose=args.verbose, - skip_pip=args.skip_pip, log_rotate_days=args.log_rotate_days) + # Run hass is child process. Restart if necessary. + keep_running = True + while keep_running: + hass_proc = Process(target=setup_and_run_hass, args=(config_dir, args)) + keep_running = run_hass_process(hass_proc) - if args.open_ui: - def open_browser(event): - """ Open the webinterface in a browser. """ - if hass.config.api is not None: - import webbrowser - webbrowser.open(hass.config.api.base_url) - - hass.bus.listen_once(EVENT_HOMEASSISTANT_START, open_browser) - - hass.start() - hass.block_till_stopped() if __name__ == "__main__": main() diff --git a/homeassistant/const.py b/homeassistant/const.py index 143704e1968..2d605a7ee71 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -10,7 +10,6 @@ MATCH_ALL = '*' DEVICE_DEFAULT_NAME = "Unnamed Device" # #### CONFIG #### -CONF_ICON = "icon" CONF_LATITUDE = "latitude" CONF_LONGITUDE = "longitude" CONF_TEMPERATURE_UNIT = "temperature_unit" @@ -124,6 +123,7 @@ ATTR_GPS_ACCURACY = 'gps_accuracy' # #### SERVICES #### SERVICE_HOMEASSISTANT_STOP = "stop" +SERVICE_HOMEASSISTANT_RESTART = "restart" SERVICE_TURN_ON = 'turn_on' SERVICE_TURN_OFF = 'turn_off' diff --git a/homeassistant/core.py b/homeassistant/core.py index 853d09020ce..e6b0a6ec722 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -16,7 +16,8 @@ from collections import namedtuple from homeassistant.const import ( __version__, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, - SERVICE_HOMEASSISTANT_STOP, EVENT_TIME_CHANGED, EVENT_STATE_CHANGED, + SERVICE_HOMEASSISTANT_STOP, SERVICE_HOMEASSISTANT_RESTART, + EVENT_TIME_CHANGED, EVENT_STATE_CHANGED, EVENT_CALL_SERVICE, ATTR_NOW, ATTR_DOMAIN, ATTR_SERVICE, MATCH_ALL, EVENT_SERVICE_EXECUTED, ATTR_SERVICE_CALL_ID, EVENT_SERVICE_REGISTERED, TEMP_CELCIUS, TEMP_FAHRENHEIT, ATTR_FRIENDLY_NAME) @@ -70,13 +71,21 @@ class HomeAssistant(object): def block_till_stopped(self): """Register service homeassistant/stop and will block until called.""" request_shutdown = threading.Event() + request_restart = threading.Event() def stop_homeassistant(*args): """Stop Home Assistant.""" request_shutdown.set() + def restart_homeassistant(*args): + """Reset Home Assistant.""" + request_restart.set() + request_shutdown.set() + self.services.register( DOMAIN, SERVICE_HOMEASSISTANT_STOP, stop_homeassistant) + self.services.register( + DOMAIN, SERVICE_HOMEASSISTANT_RESTART, restart_homeassistant) if os.name != "nt": try: @@ -92,6 +101,7 @@ class HomeAssistant(object): break self.stop() + return request_restart.isSet() def stop(self): """Stop Home Assistant and shuts down all threads."""