Implemented restart service

Implemented an OS and environment safe restart service. This works by
running Home Assistant in a child process. If the child process
terminates with an exit code > 0, HASS is restarted. SIGTERM and
KeyboardInterrupts to the parent process are forwarded to the child
process. KeyboardInterrupts will only be forwarded once. The second
KeyboardInterrupt will be handled by the parent.
This commit is contained in:
Ryan Kraus 2016-01-26 22:39:59 -05:00
parent 14cd27aaa7
commit b596fa33d6
3 changed files with 75 additions and 28 deletions

View File

@ -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()

View File

@ -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'

View File

@ -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."""