def main(self): # noqa: C901 """Initial AppDaemon entry point. Parse command line arguments, load configuration, set up logging. """ self.init_signals() # Get command line args parser = argparse.ArgumentParser() parser.add_argument( "-c", "--config", help="full path to config directory", type=str, default=None, ) parser.add_argument("-p", "--pidfile", help="full path to PID File", default=None) parser.add_argument( "-t", "--timewarp", help="speed that the scheduler will work at for time travel", default=1, type=float, ) parser.add_argument( "-s", "--starttime", help="start time for scheduler <YYYY-MM-DD HH:MM:SS|YYYY-MM-DD#HH:MM:SS>", type=str, ) parser.add_argument( "-e", "--endtime", help="end time for scheduler <YYYY-MM-DD HH:MM:SS|YYYY-MM-DD#HH:MM:SS>", type=str, default=None, ) parser.add_argument( "-D", "--debug", help="global debug level", default="INFO", choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], ) parser.add_argument("-m", "--moduledebug", nargs=2, action="append") parser.add_argument("-v", "--version", action="version", version="%(prog)s " + utils.__version__) parser.add_argument("--profiledash", help=argparse.SUPPRESS, action="store_true") args = parser.parse_args() config_dir = args.config pidfile = args.pidfile if config_dir is None: config_file_yaml = utils.find_path("appdaemon.yaml") else: config_file_yaml = os.path.join(config_dir, "appdaemon.yaml") if config_file_yaml is None: print("FATAL: no configuration directory defined and defaults not present\n") parser.print_help() sys.exit(1) module_debug = {} if args.moduledebug is not None: for arg in args.moduledebug: module_debug[arg[0]] = arg[1] # # First locate secrets file # try: # # Read config file using include directory # yaml.add_constructor("!include", utils._include_yaml, Loader=yaml.SafeLoader) # # Read config file using environment variables # yaml.add_constructor("!env_var", utils._env_var_yaml, Loader=yaml.SafeLoader) # # Initially load file to see if secret directive is present # yaml.add_constructor("!secret", utils._dummy_secret, Loader=yaml.SafeLoader) with open(config_file_yaml, "r") as yamlfd: config_file_contents = yamlfd.read() config = yaml.load(config_file_contents, Loader=yaml.SafeLoader) if "secrets" in config: secrets_file = config["secrets"] else: secrets_file = os.path.join(os.path.dirname(config_file_yaml), "secrets.yaml") # # Read Secrets # if os.path.isfile(secrets_file): with open(secrets_file, "r") as yamlfd: secrets_file_contents = yamlfd.read() utils.secrets = yaml.load(secrets_file_contents, Loader=yaml.SafeLoader) else: if "secrets" in config: print( "ERROR", "Error loading secrets file: {}".format(config["secrets"]), ) sys.exit() # # Read config file again, this time with secrets # yaml.add_constructor("!secret", utils._secret_yaml, Loader=yaml.SafeLoader) with open(config_file_yaml, "r") as yamlfd: config_file_contents = yamlfd.read() config = yaml.load(config_file_contents, Loader=yaml.SafeLoader) except yaml.YAMLError as exc: print("ERROR", "Error loading configuration") if hasattr(exc, "problem_mark"): if exc.context is not None: print("ERROR", "parser says") print("ERROR", str(exc.problem_mark)) print("ERROR", str(exc.problem) + " " + str(exc.context)) else: print("ERROR", "parser says") print("ERROR", str(exc.problem_mark)) print("ERROR", str(exc.problem)) sys.exit() if "appdaemon" not in config: print("ERROR", "no 'appdaemon' section in {}".format(config_file_yaml)) sys.exit() appdaemon = config["appdaemon"] if "disable_apps" not in appdaemon: appdaemon["disable_apps"] = False appdaemon["config_dir"] = config_dir appdaemon["config_file"] = config_file_yaml appdaemon["app_config_file"] = os.path.join(os.path.dirname(config_file_yaml), "apps.yaml") appdaemon["module_debug"] = module_debug if args.starttime is not None: appdaemon["starttime"] = args.starttime if args.endtime is not None: appdaemon["endtime"] = args.endtime if "timewarp" not in appdaemon: appdaemon["timewarp"] = args.timewarp appdaemon["loglevel"] = args.debug appdaemon["config_dir"] = os.path.dirname(config_file_yaml) appdaemon["stop_function"] = self.stop hadashboard = None if "hadashboard" in config: if config["hadashboard"] is None: hadashboard = {} else: hadashboard = config["hadashboard"] hadashboard["profile_dashboard"] = args.profiledash hadashboard["config_dir"] = config_dir hadashboard["config_file"] = config_file_yaml hadashboard["config_dir"] = os.path.dirname(config_file_yaml) if args.profiledash: hadashboard["profile_dashboard"] = True if "dashboard" not in hadashboard: hadashboard["dashboard"] = True old_admin = None if "old_admin" in config: if config["old_admin"] is None: old_admin = {} else: old_admin = config["old_admin"] admin = None if "admin" in config: if config["admin"] is None: admin = {} else: admin = config["admin"] api = None if "api" in config: if config["api"] is None: api = {} else: api = config["api"] http = None if "http" in config: http = config["http"] # Setup _logging if "log" in config: print( "ERROR", "'log' directive deprecated, please convert to new 'logs' syntax", ) sys.exit(1) if "logs" in config: logs = config["logs"] else: logs = {} self.logging = logging.Logging(logs, args.debug) self.logger = self.logging.get_logger() if "time_zone" in config["appdaemon"]: self.logging.set_tz(pytz.timezone(config["appdaemon"]["time_zone"])) # Startup message self.logger.info("AppDaemon Version %s starting", utils.__version__) self.logger.info( "Python version is %s.%s.%s", sys.version_info[0], sys.version_info[1], sys.version_info[2], ) self.logger.info("Configuration read from: %s", config_file_yaml) self.logging.dump_log_config() self.logger.debug("AppDaemon Section: %s", config.get("appdaemon")) self.logger.debug("HADashboard Section: %s", config.get("hadashboard")) exit = False if "time_zone" not in config["appdaemon"]: self.logger.error("time_zone not specified in appdaemon.yaml") exit = True if "latitude" not in config["appdaemon"]: self.logger.error("latitude not specified in appdaemon.yaml") exit = True if "longitude" not in config["appdaemon"]: self.logger.error("longitude not specified in appdaemon.yaml") exit = True if "elevation" not in config["appdaemon"]: self.logger.error("elevation not specified in appdaemon.yaml") exit = True if exit is True: sys.exit(1) utils.check_path("config_file", self.logger, config_file_yaml, pathtype="file") if pidfile is not None: self.logger.info("Using pidfile: %s", pidfile) dir = os.path.dirname(pidfile) name = os.path.basename(pidfile) try: with pid.PidFile(name, dir): self.run(appdaemon, hadashboard, old_admin, admin, api, http) except pid.PidFileError: self.logger.error("Unable to acquire pidfile - terminating") else: self.run(appdaemon, hadashboard, old_admin, admin, api, http)
def __init__(self, logging, loop, **kwargs): # # Import various AppDaemon bits and pieces now to avoid circular import # import appdaemon.utils as utils import appdaemon.thread_async as appq import appdaemon.utility_loop as utility import appdaemon.plugin_management as plugins import appdaemon.threading import appdaemon.app_management as apps import appdaemon.callbacks as callbacks import appdaemon.futures as futures import appdaemon.state as state import appdaemon.events as events import appdaemon.services as services import appdaemon.sequences as sequences import appdaemon.scheduler as scheduler self.logging = logging self.logging.register_ad(self) self.logger = logging.get_logger() self.threading = None self.callbacks = None self.futures = None self.state = None self.config = kwargs self.booted = "booting" self.config["ad_version"] = utils.__version__ self.check_app_updates_profile = "" self.was_dst = False self.last_state = None self.executor = None self.loop = None self.srv = None self.appd = None self.stopping = False self.http = None self.admin_loop = None self.global_vars = {} self.global_lock = threading.RLock() self.config_file_modified = 0 self.sched = None self.thread_async = None self.utility = None self.module_debug = kwargs["module_debug"] # User Supplied/Defaults self.load_distribution = "roundrobbin" utils.process_arg(self, "load_distribution", kwargs) self.app_dir = None utils.process_arg(self, "app_dir", kwargs) self.starttime = None utils.process_arg(self, "starttime", kwargs) self.latitude = None utils.process_arg(self, "latitude", kwargs, float=True) self.longitude = None utils.process_arg(self, "longitude", kwargs, float=True) self.elevation = None utils.process_arg(self, "elevation", kwargs, int=True) self.time_zone = None utils.process_arg(self, "time_zone", kwargs) self.tz = None self.loop = loop self.logfile = None self.errfile = None self.config_file = None utils.process_arg(self, "config_file", kwargs) self.config_dir = None utils.process_arg(self, "config_dir", kwargs) self.timewarp = 1 utils.process_arg(self, "timewarp", kwargs, float=True) self.max_clock_skew = 1 utils.process_arg(self, "max_clock_skew", kwargs, int=True) self.thread_duration_warning_threshold = 10 utils.process_arg(self, "thread_duration_warning_threshold", kwargs, float=True) self.threadpool_workers = 10 utils.process_arg(self, "threadpool_workers", kwargs, int=True) self.endtime = None utils.process_arg(self, "endtime", kwargs) self.loglevel = "INFO" utils.process_arg(self, "loglevel", kwargs) self.api_port = None utils.process_arg(self, "api_port", kwargs) self.utility_delay = 1 utils.process_arg(self, "utility_delay", kwargs, int=True) self.admin_delay = 1 utils.process_arg(self, "admin_delay", kwargs, int=True) self.max_utility_skew = self.utility_delay * 2 utils.process_arg(self, "max_utility_skew", kwargs, float=True) self.check_app_updates_profile = False utils.process_arg(self, "check_app_updates_profile", kwargs) self.production_mode = False utils.process_arg(self, "production_mode", kwargs) self.invalid_yaml_warnings = True utils.process_arg(self, "invalid_yaml_warnings", kwargs) self.missing_app_warnings = True utils.process_arg(self, "missing_app_warnings", kwargs) self.log_thread_actions = False utils.process_arg(self, "log_thread_actions", kwargs) self.qsize_warning_threshold = 50 utils.process_arg(self, "qsize_warning_threshold", kwargs, int=True) self.qsize_warning_step = 60 utils.process_arg(self, "qsize_warning_step", kwargs, int=True) self.qsize_warning_iterations = 10 utils.process_arg(self, "qsize_warning_iterations", kwargs, int=True) self.internal_function_timeout = 10 utils.process_arg(self, "internal_function_timeout", kwargs, int=True) self.namespaces = {} utils.process_arg(self, "namespaces", kwargs) self.exclude_dirs = ["__pycache__"] if "exclude_dirs" in kwargs: self.exclude_dirs += kwargs["exclude_dirs"] self.stop_function = None utils.process_arg(self, "stop_function", kwargs) if not kwargs.get("cert_verify", True): self.certpath = False if kwargs.get("disable_apps") is True: self.apps = False self.logging.log("INFO", "Apps are disabled") else: self.apps = True # # Set up services # self.services = services.Services(self) # # Set up sequences # self.sequences = sequences.Sequences(self) # # Set up scheduler # self.sched = scheduler.Scheduler(self) # # Set up state # self.state = state.State(self) # # Set up events # self.events = events.Events(self) # # Set up callbacks # self.callbacks = callbacks.Callbacks(self) # # Set up futures # self.futures = futures.Futures(self) if self.apps is True: if self.app_dir is None: if self.config_dir is None: self.app_dir = utils.find_path("apps") self.config_dir = os.path.dirname(self.app_dir) else: self.app_dir = os.path.join(self.config_dir, "apps") utils.check_path("config_dir", self.logger, self.config_dir, permissions="rwx") utils.check_path("appdir", self.logger, self.app_dir) # Initialize Apps self.app_management = apps.AppManagement( self, kwargs.get("app_config_file", None)) # threading setup self.threading = appdaemon.threading.Threading(self, kwargs) self.stopping = False # # Set up Executor ThreadPool # if "threadpool_workers" in kwargs: self.threadpool_workers = int(kwargs["threadpool_workers"]) self.executor = concurrent.futures.ThreadPoolExecutor( max_workers=self.threadpool_workers) # Initialize Plugins if "plugins" in kwargs: args = kwargs["plugins"] else: args = None self.plugins = plugins.Plugins(self, args) # Create thread_async Loop self.logger.debug("Starting thread_async loop") if self.apps is True: self.thread_async = appq.ThreadAsync(self) loop.create_task(self.thread_async.loop()) # Create utility loop self.logger.debug("Starting utility loop") self.utility = utility.Utility(self) loop.create_task(self.utility.loop())
def __init__(self, config_dir, logger, **kwargs): # # Set Defaults # self.config_dir = config_dir self.logger = logger self.dash_install_dir = os.path.dirname(__file__) self.dashboard_dir = os.path.join(config_dir, "dashboards") self.profile_dashboard = False self.compile_dir = os.path.join(self.config_dir, "compiled") self.javascript_dir = os.path.join(self.dash_install_dir, "assets", "javascript") self.compiled_javascript_dir = os.path.join(self.compile_dir, "javascript") self.compiled_html_dir = os.path.join(self.compile_dir, "html") self.template_dir = os.path.join(self.dash_install_dir, "assets", "templates") self.css_dir = os.path.join(self.dash_install_dir, "assets", "css") self.compiled_css_dir = os.path.join(self.compile_dir, "css") self.fonts_dir = os.path.join(self.dash_install_dir, "assets", "fonts") self.webfonts_dir = os.path.join(self.dash_install_dir, "assets", "webfonts") self.images_dir = os.path.join(self.dash_install_dir, "assets", "images") self.base_url = "" self.dash_force_compile = False self.dash_compile_on_start = False self.max_include_depth = 10 self.fa4compatibility = False # # Process any overrides # self._process_arg("profile_dashboard", kwargs) self._process_arg("dashboard_dir", kwargs) self._process_arg("compile_dir", kwargs) self._process_arg("javascript_dir", kwargs) self._process_arg("compiled_javascript_dir", kwargs) self._process_arg("compiled_html_dir", kwargs) self._process_arg("template_dir", kwargs) self._process_arg("css_dir", kwargs) self._process_arg("compiled_css_dir", kwargs) self._process_arg("fonts_dir", kwargs) self._process_arg("webfonts_dir", kwargs) self._process_arg("images_dir", kwargs) self._process_arg("base_url", kwargs) self._process_arg("dash_force_compile", kwargs) self._process_arg("dash_compile_on_start", kwargs) self._process_arg("max_include_depth", kwargs) self._process_arg("fa4compatibility", kwargs) # # Create some dirs # try: js = os.path.join(self.compile_dir, "javascript") css = os.path.join(self.compile_dir, "css") if not os.path.isdir(self.compile_dir): os.makedirs(self.compile_dir) if not os.path.isdir(os.path.join(self.compile_dir, "javascript")): os.makedirs(js) if not os.path.isdir(os.path.join(self.compile_dir, "css")): os.makedirs(css) ha.check_path("css", self.logger, css, permissions="rwx") ha.check_path("javascript", self.logger, js, permissions="rwx") except: ha.log(self.logger, "WARNING", '-' * 60) ha.log(self.logger, "WARNING", "Unexpected error during HADashboard initialization") ha.log(self.logger, "WARNING", '-' * 60) ha.log(self.logger, "WARNING", traceback.format_exc()) ha.log(self.logger, "WARNING", '-' * 60) # # Set a start time # self.start_time = datetime.datetime.now()
def main(self): # import appdaemon.stacktracer # appdaemon.stacktracer.trace_start("/tmp/trace.html") self.init_signals() # Get command line args parser = argparse.ArgumentParser() parser.add_argument("-c", "--config", help="full path to config directory", type=str, default=None) parser.add_argument("-p", "--pidfile", help="full path to PID File", default="/tmp/hapush.pid") parser.add_argument( "-t", "--tick", help="time that a tick in the schedular lasts (seconds)", default=1, type=float) parser.add_argument( "-s", "--starttime", help="start time for scheduler <YYYY-MM-DD HH:MM:SS>", type=str) parser.add_argument( "-e", "--endtime", help="end time for scheduler <YYYY-MM-DD HH:MM:SS>", type=str, default=None) parser.add_argument("-i", "--interval", help="multiplier for scheduler tick", type=float, default=1) parser.add_argument( "-D", "--debug", help="debug level", default="INFO", choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]) parser.add_argument('-v', '--version', action='version', version='%(prog)s ' + utils.__version__) parser.add_argument('--profiledash', help=argparse.SUPPRESS, action='store_true') # Windows does not have Daemonize package so disallow if platform.system() != "Windows": parser.add_argument("-d", "--daemon", help="run as a background process", action="store_true") args = parser.parse_args() config_dir = args.config if platform.system() != "Windows": from daemonize import Daemonize if platform.system() != "Windows": isdaemon = args.daemon else: isdaemon = False if config_dir is None: config_file_yaml = utils.find_path("appdaemon.yaml") else: config_file_yaml = os.path.join(config_dir, "appdaemon.yaml") if config_file_yaml is None: print( "FATAL: no configuration directory defined and defaults not present\n" ) parser.print_help() sys.exit(1) config = None # # First locate secrets file # try: # # Initially load file to see if secret directive is present # yaml.add_constructor('!secret', utils._dummy_secret) with open(config_file_yaml, 'r') as yamlfd: config_file_contents = yamlfd.read() config = yaml.load(config_file_contents) if "secrets" in config: secrets_file = config["secrets"] else: secrets_file = os.path.join(os.path.dirname(config_file_yaml), "secrets.yaml") # # Read Secrets # if os.path.isfile(secrets_file): with open(secrets_file, 'r') as yamlfd: secrets_file_contents = yamlfd.read() utils.secrets = yaml.load(secrets_file_contents) else: if "secrets" in config: print( "ERROR", "Error loading secrets file: {}".format( config["secrets"])) sys.exit() # # Read config file again, this time with secrets # yaml.add_constructor('!secret', utils._secret_yaml) with open(config_file_yaml, 'r') as yamlfd: config_file_contents = yamlfd.read() config = yaml.load(config_file_contents) except yaml.YAMLError as exc: print("ERROR", "Error loading configuration") if hasattr(exc, 'problem_mark'): if exc.context is not None: print("ERROR", "parser says") print("ERROR", str(exc.problem_mark)) print("ERROR", str(exc.problem) + " " + str(exc.context)) else: print("ERROR", "parser says") print("ERROR", str(exc.problem_mark)) print("ERROR", str(exc.problem)) sys.exit() if "appdaemon" not in config: print("ERROR", "no 'appdaemon' section in {}".format(config_file_yaml)) sys.exit() appdaemon = config["appdaemon"] if "disable_apps" not in appdaemon: appdaemon["disable_apps"] = False appdaemon["config_dir"] = config_dir appdaemon["config_file"] = config_file_yaml appdaemon["app_config_file"] = os.path.join( os.path.dirname(config_file_yaml), "apps.yaml") if args.starttime is not None: appdaemon["starttime"] = args.starttime if args.endtime is not None: appdaemon["endtime"] = args.endtime appdaemon["tick"] = args.tick appdaemon["interval"] = args.interval appdaemon["loglevel"] = args.debug appdaemon["config_dir"] = os.path.dirname(config_file_yaml) appdaemon["stop_function"] = self.stop if "hadashboard" in config: hadashboard = config["hadashboard"] hadashboard["profile_dashboard"] = args.profiledash hadashboard["config_dir"] = config_dir hadashboard["config_file"] = config_file_yaml hadashboard["config_dir"] = os.path.dirname(config_file_yaml) if args.profiledash: hadashboard["profile_dashboard"] = True if "dashboard" not in hadashboard: hadashboard["dashboard"] = True else: hadashboard = {"dashboard": False} if "log" not in config: logfile = "STDOUT" errorfile = "STDERR" diagfile = "STDOUT" log_size = 1000000 log_generations = 3 accessfile = None else: logfile = config['log'].get("logfile", "STDOUT") errorfile = config['log'].get("errorfile", "STDERR") diagfile = config['log'].get("diagfile", "NONE") if diagfile == "NONE": diagfile = logfile log_size = config['log'].get("log_size", 1000000) log_generations = config['log'].get("log_generations", 3) accessfile = config['log'].get("accessfile") if isdaemon and (logfile == "STDOUT" or errorfile == "STDERR" or logfile == "STDERR" or errorfile == "STDOUT"): print("ERROR", "STDOUT and STDERR not allowed with -d") sys.exit() # Setup Logging self.logger = logging.getLogger("log1") numeric_level = getattr(logging, args.debug, None) self.logger.setLevel(numeric_level) self.logger.propagate = False # formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') # Send to file if we are daemonizing, else send to console fh = None if logfile != "STDOUT": fh = RotatingFileHandler(logfile, maxBytes=log_size, backupCount=log_generations) fh.setLevel(numeric_level) # fh.setFormatter(formatter) self.logger.addHandler(fh) else: # Default for StreamHandler() is sys.stderr ch = logging.StreamHandler(stream=sys.stdout) ch.setLevel(numeric_level) # ch.setFormatter(formatter) self.logger.addHandler(ch) # Setup compile output self.error = logging.getLogger("log2") numeric_level = getattr(logging, args.debug, None) self.error.setLevel(numeric_level) self.error.propagate = False # formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') if errorfile != "STDERR": efh = RotatingFileHandler(errorfile, maxBytes=log_size, backupCount=log_generations) else: efh = logging.StreamHandler() efh.setLevel(numeric_level) # efh.setFormatter(formatter) self.error.addHandler(efh) # setup diag output self.diag = logging.getLogger("log3") numeric_level = getattr(logging, args.debug, None) self.diag.setLevel(numeric_level) self.diag.propagate = False # formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') if diagfile != "STDOUT": dfh = RotatingFileHandler(diagfile, maxBytes=log_size, backupCount=log_generations) else: dfh = logging.StreamHandler() dfh.setLevel(numeric_level) # dfh.setFormatter(formatter) self.diag.addHandler(dfh) # Setup dash output if accessfile is not None: self.access = logging.getLogger("log4") numeric_level = getattr(logging, args.debug, None) self.access.setLevel(numeric_level) self.access.propagate = False # formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') efh = RotatingFileHandler(config['log'].get("accessfile"), maxBytes=log_size, backupCount=log_generations) efh.setLevel(numeric_level) # efh.setFormatter(formatter) self.access.addHandler(efh) else: self.access = self.logger # Startup message self.log(self.logger, "INFO", "AppDaemon Version {} starting".format(utils.__version__)) self.log(self.logger, "INFO", "Configuration read from: {}".format(config_file_yaml)) self.log(self.logger, "DEBUG", "AppDaemon Section: {}".format(config.get("AppDaemon"))) self.log(self.logger, "DEBUG", "HADashboard Section: {}".format(config.get("HADashboard"))) utils.check_path("config_file", self.logger, config_file_yaml, pathtype="file") if isdaemon: keep_fds = [fh.stream.fileno(), efh.stream.fileno()] pid = args.pidfile daemon = Daemonize(app="appdaemon", pid=pid, action=self.run, keep_fds=keep_fds) daemon.start() while True: time.sleep(1) else: self.run(appdaemon, hadashboard)
def main(self): # import appdaemon.stacktracer # appdaemon.stacktracer.trace_start("/tmp/trace.html") self.init_signals() # Get command line args parser = argparse.ArgumentParser() parser.add_argument("-c", "--config", help="full path to config directory", type=str, default=None) parser.add_argument("-p", "--pidfile", help="full path to PID File", default="/tmp/hapush.pid") parser.add_argument("-t", "--tick", help="time that a tick in the schedular lasts (seconds)", default=1, type=float) parser.add_argument("-s", "--starttime", help="start time for scheduler <YYYY-MM-DD HH:MM:SS>", type=str) parser.add_argument("-e", "--endtime", help="end time for scheduler <YYYY-MM-DD HH:MM:SS>", type=str, default=None) parser.add_argument("-i", "--interval", help="multiplier for scheduler tick", type=float, default=1) parser.add_argument("-D", "--debug", help="debug level", default="INFO", choices= [ "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL" ]) parser.add_argument('-v', '--version', action='version', version='%(prog)s ' + utils.__version__) parser.add_argument('--profiledash', help=argparse.SUPPRESS, action='store_true') # Windows does not have Daemonize package so disallow if platform.system() != "Windows": parser.add_argument("-d", "--daemon", help="run as a background process", action="store_true") args = parser.parse_args() config_dir = args.config if platform.system() != "Windows": from daemonize import Daemonize if platform.system() != "Windows": isdaemon = args.daemon else: isdaemon = False if config_dir is None: config_file_yaml = utils.find_path("appdaemon.yaml") else: config_file_yaml = os.path.join(config_dir, "appdaemon.yaml") if config_file_yaml is None: print("FATAL: no configuration directory defined and defaults not present\n") parser.print_help() sys.exit(1) config = None # # First locate secrets file # try: # # Initially load file to see if secret directive is present # yaml.add_constructor('!secret', utils._dummy_secret, Loader=yaml.SafeLoader) with open(config_file_yaml, 'r') as yamlfd: config_file_contents = yamlfd.read() config = yaml.load(config_file_contents, Loader=yaml.SafeLoader) if "secrets" in config: secrets_file = config["secrets"] else: secrets_file = os.path.join(os.path.dirname(config_file_yaml), "secrets.yaml") # # Read Secrets # if os.path.isfile(secrets_file): with open(secrets_file, 'r') as yamlfd: secrets_file_contents = yamlfd.read() utils.secrets = yaml.load(secrets_file_contents, Loader=yaml.SafeLoader) else: if "secrets" in config: print("ERROR", "Error loading secrets file: {}".format(config["secrets"])) sys.exit() # # Read config file again, this time with secrets # yaml.add_constructor('!secret', utils._secret_yaml, Loader=yaml.SafeLoader) with open(config_file_yaml, 'r') as yamlfd: config_file_contents = yamlfd.read() config = yaml.load(config_file_contents, Loader=yaml.SafeLoader) except yaml.YAMLError as exc: print("ERROR", "Error loading configuration") if hasattr(exc, 'problem_mark'): if exc.context is not None: print("ERROR", "parser says") print("ERROR", str(exc.problem_mark)) print("ERROR", str(exc.problem) + " " + str(exc.context)) else: print("ERROR", "parser says") print("ERROR", str(exc.problem_mark)) print("ERROR", str(exc.problem)) sys.exit() if "appdaemon" not in config: print("ERROR", "no 'appdaemon' section in {}".format(config_file_yaml)) sys.exit() appdaemon = config["appdaemon"] if "disable_apps" not in appdaemon: appdaemon["disable_apps"] = False appdaemon["config_dir"] = config_dir appdaemon["config_file"] = config_file_yaml appdaemon["app_config_file"] = os.path.join(os.path.dirname(config_file_yaml), "apps.yaml") if args.starttime is not None: appdaemon["starttime"] = args.starttime if args.endtime is not None: appdaemon["endtime"] = args.endtime appdaemon["tick"] = args.tick appdaemon["interval"] = args.interval appdaemon["loglevel"] = args.debug appdaemon["config_dir"] = os.path.dirname(config_file_yaml) appdaemon["stop_function"] = self.stop if "hadashboard" in config: hadashboard = config["hadashboard"] hadashboard["profile_dashboard"] = args.profiledash hadashboard["config_dir"] = config_dir hadashboard["config_file"] = config_file_yaml hadashboard["config_dir"] = os.path.dirname(config_file_yaml) if args.profiledash: hadashboard["profile_dashboard"] = True if "dashboard" not in hadashboard: hadashboard["dashboard"] = True else: hadashboard = {"dashboard": False} if "log" not in config: logfile = "STDOUT" errorfile = "STDERR" diagfile = "STDOUT" log_size = 1000000 log_generations = 3 accessfile = None else: logfile = config['log'].get("logfile", "STDOUT") errorfile = config['log'].get("errorfile", "STDERR") diagfile = config['log'].get("diagfile", "NONE") if diagfile == "NONE": diagfile = logfile log_size = config['log'].get("log_size", 1000000) log_generations = config['log'].get("log_generations", 3) accessfile = config['log'].get("accessfile") if isdaemon and ( logfile == "STDOUT" or errorfile == "STDERR" or logfile == "STDERR" or errorfile == "STDOUT" ): print("ERROR", "STDOUT and STDERR not allowed with -d") sys.exit() # Setup Logging self.logger = logging.getLogger("log1") numeric_level = getattr(logging, args.debug, None) self.logger.setLevel(numeric_level) self.logger.propagate = False # formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') # Send to file if we are daemonizing, else send to console fh = None if logfile != "STDOUT": fh = RotatingFileHandler(logfile, maxBytes=log_size, backupCount=log_generations) fh.setLevel(numeric_level) # fh.setFormatter(formatter) self.logger.addHandler(fh) else: # Default for StreamHandler() is sys.stderr ch = logging.StreamHandler(stream=sys.stdout) ch.setLevel(numeric_level) # ch.setFormatter(formatter) self.logger.addHandler(ch) # Setup compile output self.error = logging.getLogger("log2") numeric_level = getattr(logging, args.debug, None) self.error.setLevel(numeric_level) self.error.propagate = False # formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') if errorfile != "STDERR": efh = RotatingFileHandler( errorfile, maxBytes=log_size, backupCount=log_generations ) else: efh = logging.StreamHandler() efh.setLevel(numeric_level) # efh.setFormatter(formatter) self.error.addHandler(efh) # setup diag output self.diag = logging.getLogger("log3") numeric_level = getattr(logging, args.debug, None) self.diag.setLevel(numeric_level) self.diag.propagate = False # formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') if diagfile != "STDOUT": dfh = RotatingFileHandler( diagfile, maxBytes=log_size, backupCount=log_generations ) else: dfh = logging.StreamHandler() dfh.setLevel(numeric_level) # dfh.setFormatter(formatter) self.diag.addHandler(dfh) # Setup dash output if accessfile is not None: self.access = logging.getLogger("log4") numeric_level = getattr(logging, args.debug, None) self.access.setLevel(numeric_level) self.access.propagate = False # formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') efh = RotatingFileHandler( config['log'].get("accessfile"), maxBytes=log_size, backupCount=log_generations ) efh.setLevel(numeric_level) # efh.setFormatter(formatter) self.access.addHandler(efh) else: self.access = self.logger # Startup message self.log(self.logger, "INFO", "AppDaemon Version {} starting".format(utils.__version__)) self.log(self.logger, "INFO", "Configuration read from: {}".format(config_file_yaml)) self.log(self.logger, "DEBUG", "AppDaemon Section: {}".format(config.get("AppDaemon"))) self.log(self.logger, "DEBUG", "HADashboard Section: {}".format(config.get("HADashboard"))) utils.check_path("config_file", self.logger, config_file_yaml, pathtype="file") if isdaemon: keep_fds = [fh.stream.fileno(), efh.stream.fileno()] pid = args.pidfile daemon = Daemonize(app="appdaemon", pid=pid, action=self.run, keep_fds=keep_fds) daemon.start() while True: time.sleep(1) else: self.run(appdaemon, hadashboard)
def __init__(self, config_dir, logger, **kwargs): # # Set Defaults # self.config_dir = config_dir self.logger = logger self.dash_install_dir = os.path.dirname(__file__) self.dashboard_dir = os.path.join(config_dir, "dashboards") self.profile_dashboard = False self.compile_dir = os.path.join(self.config_dir, "compiled") self.javascript_dir = os.path.join(self.dash_install_dir, "assets", "javascript") self.compiled_javascript_dir = os.path.join(self.compile_dir, "javascript") self.compiled_html_dir = os.path.join(self.compile_dir, "html") self.template_dir = os.path.join(self.dash_install_dir, "assets", "templates") self.css_dir = os.path.join(self.dash_install_dir, "assets", "css") self.compiled_css_dir = os.path.join(self.compile_dir, "css") self.fonts_dir = os.path.join(self.dash_install_dir, "assets", "fonts") self.webfonts_dir = os.path.join(self.dash_install_dir, "assets", "webfonts") self.images_dir = os.path.join(self.dash_install_dir, "assets", "images") self.base_url = "" self.dash_force_compile = False self.dash_compile_on_start = False self.max_include_depth = 10 self.fa4compatibility = False # # Process any overrides # self._process_arg("profile_dashboard", kwargs) self._process_arg("dashboard_dir", kwargs) self._process_arg("compile_dir", kwargs) self._process_arg("javascript_dir", kwargs) self._process_arg("compiled_javascript_dir", kwargs) self._process_arg("compiled_html_dir", kwargs) self._process_arg("template_dir", kwargs) self._process_arg("css_dir", kwargs) self._process_arg("compiled_css_dir", kwargs) self._process_arg("fonts_dir", kwargs) self._process_arg("webfonts_dir", kwargs) self._process_arg("images_dir", kwargs) self._process_arg("base_url", kwargs) self._process_arg("dash_force_compile", kwargs) self._process_arg("dash_compile_on_start", kwargs) self._process_arg("max_include_depth", kwargs) self._process_arg("fa4compatibility", kwargs) # # Create some dirs # try: js = os.path.join(self.compile_dir, "javascript") css = os.path.join(self.compile_dir, "css") if not os.path.isdir(self.compile_dir): os.makedirs(self.compile_dir) if not os.path.isdir(os.path.join(self.compile_dir, "javascript")): os.makedirs(js) if not os.path.isdir(os.path.join(self.compile_dir, "css")): os.makedirs(css) ha.check_path("css", self.logger, css, permissions="rwx") ha.check_path("javascript", self.logger, js, permissions="rwx") except: ha.log(self.logger, "WARNING", '-' * 60) ha.log(self.logger, "WARNING", "Unexpected error during HADashboard initialization") ha.log(self.logger, "WARNING", '-' * 60) ha.log(self.logger, "WARNING", traceback.format_exc()) ha.log(self.logger, "WARNING", '-' * 60) # # Set a start time # self.start_time = datetime.datetime.now()