mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 00:37:53 +00:00
commit
1a5d18fd66
@ -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,64 @@ 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 SIGTERM. Are you running in a thread?')
|
||||
|
||||
hass_proc.start()
|
||||
try:
|
||||
hass_proc.join()
|
||||
except KeyboardInterrupt:
|
||||
request_stop()
|
||||
try:
|
||||
hass_proc.join()
|
||||
except KeyboardInterrupt:
|
||||
return False
|
||||
return not requested_stop.isSet() and hass_proc.exitcode == 100
|
||||
|
||||
|
||||
def main():
|
||||
""" Starts Home Assistant. """
|
||||
validate_python()
|
||||
@ -233,33 +294,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 as 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()
|
||||
|
@ -126,6 +126,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'
|
||||
|
@ -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, ATTR_SERVICE_DATA)
|
||||
@ -47,6 +48,9 @@ _LOGGER = logging.getLogger(__name__)
|
||||
# Temporary to support deprecated methods
|
||||
_MockHA = namedtuple("MockHomeAssistant", ['bus'])
|
||||
|
||||
# The exit code to send to request a restart
|
||||
RESTART_EXIT_CODE = 100
|
||||
|
||||
|
||||
class HomeAssistant(object):
|
||||
"""Root object of the Home Assistant home automation."""
|
||||
@ -70,28 +74,33 @@ 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:
|
||||
signal.signal(signal.SIGTERM, stop_homeassistant)
|
||||
except ValueError:
|
||||
_LOGGER.warning(
|
||||
'Could not bind to SIGQUIT. Are you running in a thread?')
|
||||
try:
|
||||
signal.signal(signal.SIGTERM, stop_homeassistant)
|
||||
except ValueError:
|
||||
_LOGGER.warning(
|
||||
'Could not bind to SIGTERM. Are you running in a thread?')
|
||||
|
||||
while not request_shutdown.isSet():
|
||||
try:
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
break
|
||||
time.sleep(1)
|
||||
|
||||
self.stop()
|
||||
return RESTART_EXIT_CODE if request_restart.isSet() else 0
|
||||
|
||||
def stop(self):
|
||||
"""Stop Home Assistant and shuts down all threads."""
|
||||
|
@ -7,6 +7,7 @@ Provides tests to verify that Home Assistant core works.
|
||||
# pylint: disable=protected-access,too-many-public-methods
|
||||
# pylint: disable=too-few-public-methods
|
||||
import os
|
||||
import signal
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
import time
|
||||
@ -79,15 +80,15 @@ class TestHomeAssistant(unittest.TestCase):
|
||||
|
||||
self.assertFalse(blocking_thread.is_alive())
|
||||
|
||||
def test_stopping_with_keyboardinterrupt(self):
|
||||
def test_stopping_with_sigterm(self):
|
||||
calls = []
|
||||
self.hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP,
|
||||
lambda event: calls.append(1))
|
||||
|
||||
def raise_keyboardinterrupt(length):
|
||||
raise KeyboardInterrupt
|
||||
def send_sigterm(length):
|
||||
os.kill(os.getpid(), signal.SIGTERM)
|
||||
|
||||
with patch('homeassistant.core.time.sleep', raise_keyboardinterrupt):
|
||||
with patch('homeassistant.core.time.sleep', send_sigterm):
|
||||
self.hass.block_till_stopped()
|
||||
|
||||
self.assertEqual(1, len(calls))
|
||||
|
Loading…
x
Reference in New Issue
Block a user