From e57eb2db988b9e8b78bd3bb56409ac2aecfea48b Mon Sep 17 00:00:00 2001 From: Morg42 <43153739+Morg42@users.noreply.github.com> Date: Thu, 18 Jul 2024 16:07:18 +0200 Subject: [PATCH 1/8] move templates to new template dir, adjust api_files --- lib/smarthome.py | 1 + modules/admin/api_files.py | 3 ++- templates/logic.tpl | 19 +++++++++++++++++++ templates/uf.tpl | 25 +++++++++++++++++++++++++ 4 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 templates/logic.tpl create mode 100644 templates/uf.tpl diff --git a/lib/smarthome.py b/lib/smarthome.py index c407c4fb9f..0f2701f76b 100644 --- a/lib/smarthome.py +++ b/lib/smarthome.py @@ -168,6 +168,7 @@ def initialize_dir_vars(self): # env and var dirs self._env_dir = os.path.join(self._lib_dir, 'env' + os.path.sep) + self._template_dir = os.path.join(self._base_dir, 'templates') self._env_logic_conf_basename = os.path.join(self._env_dir, 'logic') self._cache_dir = os.path.join(self._var_dir, 'cache' + os.path.sep) diff --git a/modules/admin/api_files.py b/modules/admin/api_files.py index 255aa5b4a1..79b2c4cc3c 100644 --- a/modules/admin/api_files.py +++ b/modules/admin/api_files.py @@ -56,6 +56,7 @@ def __init__(self, module): self.functions_dir = self._sh.get_functionsdir() self.scenes_dir = self._sh._scenes_dir self.logics_dir = self._sh.get_logicsdir() + self.template_dir = self._sh._template_dir self.extern_conf_dir = self._sh._extern_conf_dir self.modules_dir = os.path.join(self.base_dir, 'modules') return @@ -337,7 +338,7 @@ def get_functions_config(self, fn): self.logger.info("FilesController.get_functions_config({})".format(fn)) if fn.endswith('.tpl'): - filename = os.path.join(self.functions_dir, fn) + filename = os.path.join(self.template_dir, fn) else: filename = os.path.join(self.functions_dir, fn + '.py') read_data = None diff --git a/templates/logic.tpl b/templates/logic.tpl new file mode 100644 index 0000000000..280e34fa01 --- /dev/null +++ b/templates/logic.tpl @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +# +# This file contains a logic for use with SmartHomeNG +# +# Name of the logic: example_logic.py +# + +# This logic performs the following function(s): +# +# ... +# + +# The following triggers should be defined in ../etc/logic.yaml: +# +# watch_item = | | ... +# crontab = init = Init +# cycle = 600 +# + diff --git a/templates/uf.tpl b/templates/uf.tpl new file mode 100644 index 0000000000..dd523d0aad --- /dev/null +++ b/templates/uf.tpl @@ -0,0 +1,25 @@ +# +# This file contains user defined functions for use with SmartHomeNG +# +import logging +_logger = logging.getLogger(__name__) + +_VERSION = '0.1.0' +_DESCRIPTION = 'Per Anhalter durch die Galaxis' + +# +# Example functions +# +def zweiundvierzig(): + + return 'Die Antwort auf die Frage aller Fragen' + +def itemtest(sh): + + return sh.env.location.sun_position.elevation.degrees() + +def log_test(): + + _logger.warning('Log-Test aus einer Userfunction') + + return From 03cd2307e5dda377ee7a5fb199c538f222e7f5e2 Mon Sep 17 00:00:00 2001 From: Morg42 <43153739+Morg42@users.noreply.github.com> Date: Mon, 22 Jul 2024 11:46:52 +0200 Subject: [PATCH 2/8] api_logics: use logic.tpl --- modules/admin/api_logics.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/modules/admin/api_logics.py b/modules/admin/api_logics.py index 1db6703428..6e7cb707d5 100644 --- a/modules/admin/api_logics.py +++ b/modules/admin/api_logics.py @@ -50,6 +50,7 @@ def __init__(self, module): self.etc_dir = self._sh._etc_dir self.logics_dir = self._sh.get_logicsdir() + self.template_dir = self._sh._template_dir self.logics = Logics.get_instance() self.logger.info("__init__ self.logics = {}".format(self.logics)) self.plugins = Plugins.get_instance() @@ -318,7 +319,6 @@ def logic_create_codefile(self, filename, logics_code, overwrite=False): f = open(pathname, 'w', encoding='UTF-8') f.write(logics_code) f.close() - return True @@ -331,7 +331,6 @@ def logic_create_config(self, logicname, filename): config_list.append(['enabled', False, '']) self.logics.update_config_section(True, logicname, config_list) # self.logics.set_config_section_key(logicname, 'visu_acl', False) - return def get_logic_state(self, logicname): @@ -349,6 +348,17 @@ def get_logic_state(self, logicname): return json.dumps(logic_status) + def get_logic_template(self, logicname): + filename = os.path.join(self.template_dir, 'logic.tpl') + read_data = None + try: + with open(filename, encoding='UTF-8') as f: + read_data = f.read().replace('example_logic.py', logicname) + except Exception: + read_data = '#!/usr/bin/env python3\n' + '# ' + logicname + '\n\n' + return read_data + + def set_logic_state(self, logicname, action, filename): """ @@ -405,7 +415,7 @@ def set_logic_state(self, logicname, action, filename): else: if not os.path.isfile(os.path.join(self.logics.get_logics_dir(), filename)): #create new logic code file, if none is found - logics_code = '#!/usr/bin/env python3\n' + '# ' + filename + '\n\n' + logics_code = self.get_logic_template(filename) if not self.logic_create_codefile(filename, logics_code): self.logger.error(f"Could not create code-file '{filename}'") return json.dumps({"result": "error", "description": f"Could not create code-file '{filename}'"}) From b68f71f776d58b6ecfdf95d681be03e740b7ab37 Mon Sep 17 00:00:00 2001 From: Morg42 <43153739+Morg42@users.noreply.github.com> Date: Tue, 23 Jul 2024 17:03:31 +0200 Subject: [PATCH 3/8] core: replace static file/dir references by constants --- lib/backup.py | 84 +++++++++++++++++++++----------------------- lib/config.py | 9 +++-- lib/constants.py | 25 +++++++++++++ lib/item/structs.py | 15 ++++---- lib/log.py | 13 +++---- lib/logic.py | 18 +++++----- lib/metadata.py | 6 ++-- lib/module.py | 6 ++-- lib/plugin.py | 6 ++-- lib/scene.py | 9 ++--- lib/shtime.py | 4 +-- lib/smarthome.py | 38 ++++++++++---------- lib/userfunctions.py | 3 +- 13 files changed, 131 insertions(+), 105 deletions(-) diff --git a/lib/backup.py b/lib/backup.py index 78e72b0c00..81f08e2377 100644 --- a/lib/backup.py +++ b/lib/backup.py @@ -33,6 +33,7 @@ from datetime import datetime from lib.shtime import Shtime from lib.shpypi import Shpypi +from lib.constants import (BASE_LOG, BASE_LOGIC, BASE_MODULE, BASE_PLUGIN, BASE_SH, BASE_STRUCT, BASE_HOLIDAY, DIR_ETC, DIR_ITEMS, DIR_LOGICS, DIR_SCENES, DIR_STRUCTS, DIR_UF, DIR_VAR, YAML_FILE, CONF_FILE) logger = logging.getLogger(__name__) @@ -59,8 +60,8 @@ def make_backup_directories(base_dir): :return: """ global backup_dir - backup_dir = os.path.join(base_dir, 'var','backup') - restore_dir = os.path.join(base_dir, 'var','restore') + backup_dir = os.path.join(base_dir, DIR_VAR, 'backup') + restore_dir = os.path.join(base_dir, DIR_VAR, 'restore') if not os.path.isdir(backup_dir): try: @@ -95,7 +96,7 @@ def create_backup(conf_base_dir, base_dir, filename_with_timestamp=False, before """ make_backup_directories(base_dir) - backup_dir = os.path.join(base_dir, 'var','backup') + backup_dir = os.path.join(base_dir, DIR_VAR,'backup') backup_filename = 'shng_config_backup' if before_restore: @@ -111,12 +112,12 @@ def create_backup(conf_base_dir, base_dir, filename_with_timestamp=False, before else: conf_dir = base_dir - items_dir = os.path.join(conf_dir, 'items') - logic_dir = os.path.join(conf_dir, 'logics') - scenes_dir = os.path.join(conf_dir, 'scenes') - structs_dir = os.path.join(conf_dir, 'structs') - uf_dir = os.path.join(conf_dir, 'functions') - var_dir = os.path.join(conf_dir, 'var') + items_dir = os.path.join(conf_dir, DIR_ITEMS) + logic_dir = os.path.join(conf_dir, DIR_LOGICS) + scenes_dir = os.path.join(conf_dir, DIR_SCENES) + structs_dir = os.path.join(conf_dir, DIR_STRUCTS) + uf_dir = os.path.join(conf_dir, DIR_UF) + var_dir = os.path.join(conf_dir, DIR_VAR) # create new zip file #backupzip = zipfile.ZipFile(backup_dir + os.path.sep + backup_filename, mode='w') @@ -127,16 +128,16 @@ def create_backup(conf_base_dir, base_dir, filename_with_timestamp=False, before #logger.warning("- etc_dir = {}".format(etc_dir)) source_dir = etc_dir arc_dir = 'etc' - backup_file(backupzip, source_dir, arc_dir, 'holidays.yaml') - backup_file(backupzip, source_dir, arc_dir, 'logging.yaml') - backup_file(backupzip, source_dir, arc_dir, 'logic.yaml') - backup_file(backupzip, source_dir, arc_dir, 'module.yaml') - backup_file(backupzip, source_dir, arc_dir, 'plugin.yaml') - backup_file(backupzip, source_dir, arc_dir, 'smarthome.yaml') - backup_file(backupzip, source_dir, arc_dir, 'admin.yaml') - - backup_file(backupzip, source_dir, arc_dir, 'struct.yaml') - struct_files = glob.glob(os.path.join( etc_dir, 'struct_*.yaml')) + backup_file(backupzip, source_dir, arc_dir, BASE_HOLIDAY + YAML_FILE) + backup_file(backupzip, source_dir, arc_dir, BASE_LOG + YAML_FILE) + backup_file(backupzip, source_dir, arc_dir, BASE_LOGIC + YAML_FILE) + backup_file(backupzip, source_dir, arc_dir, BASE_MODULE + YAML_FILE) + backup_file(backupzip, source_dir, arc_dir, BASE_PLUGIN + YAML_FILE) + backup_file(backupzip, source_dir, arc_dir, BASE_SH + YAML_FILE) + backup_file(backupzip, source_dir, arc_dir, 'admin' + YAML_FILE) + + backup_file(backupzip, source_dir, arc_dir, BASE_STRUCT + YAML_FILE) + struct_files = glob.glob(os.path.join( etc_dir, BASE_STRUCT + '_*' + YAML_FILE)) for pn in struct_files: fn = os.path.split(pn)[1] backup_file(backupzip, source_dir, arc_dir, fn) @@ -158,12 +159,12 @@ def create_backup(conf_base_dir, base_dir, filename_with_timestamp=False, before # backup files from /scenes #logger.warning("- scenes_dir = {}".format(scenes_dir)) - backup_directory(backupzip, scenes_dir, '.yaml') - backup_directory(backupzip, scenes_dir, '.conf') + backup_directory(backupzip, scenes_dir, YAML_FILE) + backup_directory(backupzip, scenes_dir, CONF_FILE) # backup files from /structs #logger.warning("- structs_dir = {}".format(structs_dir)) - backup_directory(backupzip, structs_dir, '.yaml') + backup_directory(backupzip, structs_dir, YAML_FILE) # backup files from /functions #logger.warning("- uf_dir = {}".format(uf_dir)) @@ -172,7 +173,7 @@ def create_backup(conf_base_dir, base_dir, filename_with_timestamp=False, before # backup files for infos-directory source_dir = var_dir arc_dir = 'infos' - backup_file(backupzip, source_dir, arc_dir, 'systeminfo.yaml') + backup_file(backupzip, source_dir, arc_dir, 'systeminfo' + YAML_FILE) shpypi = Shpypi.get_instance() if shpypi is None: @@ -237,20 +238,20 @@ def backup_file(backupzip, source_dir, arc_dir, filename): return -def backup_directory(backupzip, source_dir, extenstion='.yaml'): +def backup_directory(backupzip, source_dir, extension=YAML_FILE): """ Backup all files with a certain extension from the given directory to a zip-archive :param backupzip: Name of the zip-archive (full pathname) :param source_dir: Directory where the yaml-files to backup are located - :param extenstion: Extension of the files to backup (default is .yaml) + :param extension: Extension of the files to backup (default is .yaml) """ path = source_dir.split(os.path.sep) dir = path[len(path)-1] arc_dir = dir + os.path.sep files = [] for filename in os.listdir(source_dir): - if filename.endswith(extenstion) or extenstion == '.*': + if filename.endswith(extension) or extension == '.*': backup_file(backupzip, source_dir, arc_dir, filename) return @@ -283,11 +284,11 @@ def restore_backup(conf_base_dir, base_dir, config_etc=False): else: conf_dir = base_dir - items_dir = os.path.join(conf_dir, 'items') - logic_dir = os.path.join(conf_dir, 'logics') - scenes_dir = os.path.join(conf_dir, 'scenes') - structs_dir = os.path.join(conf_dir, 'structs') - uf_dir = os.path.join(conf_dir, 'functions') + items_dir = os.path.join(conf_dir, DIR_ITEMS) + logic_dir = os.path.join(conf_dir, DIR_LOGICS) + scenes_dir = os.path.join(conf_dir, DIR_SCENES) + structs_dir = os.path.join(conf_dir, DIR_STRUCTS) + uf_dir = os.path.join(conf_dir, DIR_UF) archive_file = '' for filename in os.listdir(restore_dir): @@ -307,7 +308,8 @@ def restore_backup(conf_base_dir, base_dir, config_etc=False): return restorezip_filename = os.path.join(restore_dir, archive_file) - logger.notice(f"Restoring configuration from file {restorezip_filename}") + # TODO: as logger has no "notice" method, for the time being log as "info"" + logger.info(f"Restoring configuration from file {restorezip_filename}") logger.info(f"Creating a backup of the current configuration before restoring configuration from zip-file") create_backup(conf_base_dir, base_dir, True, True) @@ -318,22 +320,22 @@ def restore_backup(conf_base_dir, base_dir, config_etc=False): #restorezip = zipfile.ZipFile(restorezip_filename, mode='r', compression=zipfile.ZIP_STORED) # restore files to /etc - restore_directory(restorezip, 'etc', etc_dir, overwrite) + restore_directory(restorezip, DIR_ETC, etc_dir, overwrite) # restore files to /items - restore_directory(restorezip, 'items', items_dir, overwrite) + restore_directory(restorezip, DIR_ITEMS, items_dir, overwrite) # backup files from /logic - restore_directory(restorezip, 'logics', logic_dir, overwrite) + restore_directory(restorezip, DIR_LOGICS, logic_dir, overwrite) # backup files from /scenes - restore_directory(restorezip, 'scenes', scenes_dir, overwrite) + restore_directory(restorezip, DIR_SCENES, scenes_dir, overwrite) # backup files from /structs - restore_directory(restorezip, 'structs', structs_dir, overwrite) + restore_directory(restorezip, DIR_STRUCTS, structs_dir, overwrite) # backup files from /functions - restore_directory(restorezip, 'functions', uf_dir, overwrite) + restore_directory(restorezip, DIR_UF, uf_dir, overwrite) # mark zip-file as restored logger.info(f"- marking zip-file as restored") @@ -379,9 +381,6 @@ def restore_file(restorezip, arc_dir, filename, dest_dir, overwrite=False): os.utime(os.path.join(dest_dir, filename), (date_time, date_time)) - return - - def restore_directory(restorezip, arc_dir, dest_dir, overwrite=False): """ Restore all files from a certain archive directory to a given destination directory @@ -395,7 +394,6 @@ def restore_directory(restorezip, arc_dir, dest_dir, overwrite=False): for fn in restorezip.namelist(): if fn.startswith(arc_dir+'/'): restore_file(restorezip, arc_dir, os.path.basename(fn), dest_dir, overwrite) - return def write_lastbackuptime(timestamp, timefile): @@ -407,4 +405,4 @@ def write_lastbackuptime(timestamp, timefile): :type pid: int :type pidfile: str """ - + pass diff --git a/lib/config.py b/lib/config.py index 0fdeddb43b..85fdebaa58 100644 --- a/lib/config.py +++ b/lib/config.py @@ -66,11 +66,10 @@ def parse_basename(basename, configtype=''): if config == {}: config = parse(basename + CONF_FILE) if config == {}: - if not (configtype == 'logics'): - if configtype == 'module': - logger.warning(f"No valid file '{basename}.yaml' found with {configtype} configuration") - else: - logger.error(f"No valid file '{basename}.*' found with {configtype} configuration") + if configtype == 'module': + logger.warning(f"No valid file '{basename}{YAML_FILE}' found with {configtype} configuration") + elif configtype != 'logics': + logger.error(f"No valid file '{basename}.*' found with {configtype} configuration") return config diff --git a/lib/constants.py b/lib/constants.py index 72a2a973e9..8c60cefe7b 100644 --- a/lib/constants.py +++ b/lib/constants.py @@ -91,6 +91,31 @@ YAML_FILE = '.yaml' DEFAULT_FILE = '.default' +DIR_VAR = 'var' +DIR_LIB = 'lib' +DIR_CACHE = 'cache' +DIR_ENV = 'env' +DIR_TPL = 'templates' +DIR_PLUGINS = 'plugins' +DIR_MODULES = 'modules' +DIR_ETC = 'etc' +DIR_ITEMS = 'items' +DIR_STRUCTS = 'structs' +DIR_LOGICS = 'logics' +DIR_UF = 'functions' +DIR_SCENES = 'scenes' + +BASE_SH = 'smarthome' +BASE_LOG = 'logging' +BASE_MODULE = 'module' +BASE_PLUGIN = 'plugin' +BASE_LOGIC = 'logic' +BASE_STRUCT = 'struct' +BASE_HOLIDAY = 'holidays' + +DIRS = (DIR_VAR, DIR_LIB, DIR_CACHE, DIR_ENV, DIR_TPL, DIR_PLUGINS, DIR_MODULES, DIR_ETC, DIR_ITEMS, DIR_STRUCTS, DIR_LOGICS, DIR_UF, DIR_SCENES) +BASES = (BASE_SH, BASE_LOG, BASE_MODULE, BASE_PLUGIN, BASE_LOGIC) +FILES = DIRS + BASES #attributes for 'autotimer' parameter KEY_ATTRIB_COMPAT = 'assign_compatibility' # name of key in smarthome.yaml diff --git a/lib/item/structs.py b/lib/item/structs.py index 4a40ffc840..71aad6c407 100644 --- a/lib/item/structs.py +++ b/lib/item/structs.py @@ -26,6 +26,7 @@ import lib.shyaml as shyaml from lib.config import sanitize_items +from lib.constants import (YAML_FILE, BASE_STRUCT, DIR_STRUCTS) logger = logging.getLogger(__name__) @@ -85,12 +86,12 @@ def load_definitions_from_etc(self): - structs are read in from ../etc/struct.yaml by this procedure - further structs are read in from ../etc/struct_.yaml by this procedure """ - self.load_struct_definitions_from_file(self.etc_dir, 'struct.yaml', '') + self.load_struct_definitions_from_file(self.etc_dir, BASE_STRUCT + YAML_FILE, '') # look for further struct files file_list = os.listdir(self.etc_dir) for filename in file_list: - if filename.startswith('struct_') and filename.endswith('.yaml'): + if filename.startswith(BASE_STRUCT + '_') and filename.endswith(YAML_FILE): key_prefix = 'my.' + filename[7:-5] self.load_struct_definitions_from_file(self.etc_dir, filename, key_prefix) return @@ -117,11 +118,11 @@ def migrate_to_structs_dir(self): """ fl = os.listdir(self.etc_dir) for filename in fl: - if filename.startswith('struct_') and filename.endswith('.yaml'): + if filename.startswith(BASE_STRUCT + '_') and filename.endswith(YAML_FILE): dest_fn = filename[7:] self.migrate_one_file(filename, dest_fn) - filename = 'struct.yaml' + filename = BASE_STRUCT + YAML_FILE if os.path.isfile(os.path.join(self.etc_dir, filename)): dest_fn = 'global_structs.yaml' self.migrate_one_file(filename, dest_fn) @@ -135,19 +136,19 @@ def load_definitions_from_structs(self): - structs are read in from ../structs/.yaml by this procedure """ - self.load_struct_definitions_from_file(self.etc_dir, 'struct.yaml', '') + self.load_struct_definitions_from_file(self.etc_dir, BASE_STRUCT + YAML_FILE, '') if os.path.isdir(self.structs_dir): # look for struct files file_list = os.listdir(self.structs_dir) for filename in file_list: - if not (filename.startswith('.')) and filename.endswith('.yaml'): + if not (filename.startswith('.')) and filename.endswith(YAML_FILE): if filename == 'global_structs.yaml': key_prefix = '' else: key_prefix = 'my.' + filename[:-5] self.load_struct_definitions_from_file(self.structs_dir, filename, key_prefix) else: - self.logger.notice("../structs does not exist") + self.logger.notice("../" + DIR_STRUCTS + " does not exist") return diff --git a/lib/log.py b/lib/log.py index dc2ed70c61..ee162f20b1 100644 --- a/lib/log.py +++ b/lib/log.py @@ -34,6 +34,7 @@ import collections import lib.shyaml as shyaml +from lib.constants import YAML_FILE, DEFAULT_FILE, BASE_LOG logs_instance = None @@ -67,7 +68,7 @@ def __init__(self, sh): return - def configure_logging(self, config_filename='logging.yaml'): + def configure_logging(self, config_filename=BASE_LOG + YAML_FILE): config_dict = self.load_logging_config(config_filename, ignore_notfound=True) @@ -237,15 +238,15 @@ def return_logs(self): # --------------------------------------------------------------------------- - def load_logging_config(self, filename='logging', ignore_notfound=False): + def load_logging_config(self, filename=BASE_LOG, ignore_notfound=False): """ Load config from logging.yaml to a dict If logging.yaml does not contain a 'shng_version' key, a backup is created """ conf_filename = os.path.join(self._sh.get_etcdir(), filename) - if not conf_filename.endswith('.yaml') and not conf_filename.endswith('.default'): - conf_filename += '.yaml' + if not conf_filename.endswith(YAML_FILE) and not conf_filename.endswith(DEFAULT_FILE): + conf_filename += YAML_FILE result = shyaml.yaml_load(conf_filename, ignore_notfound) return result @@ -257,7 +258,7 @@ def save_logging_config(self, logging_config, create_backup=False): """ if logging_config is not None: logging_config['shng_version'] = self._sh.version.split('-')[0][1:] - conf_filename = os.path.join(self._sh.get_etcdir(), 'logging') + conf_filename = os.path.join(self._sh.get_etcdir(), BASE_LOG) shyaml.yaml_save_roundtrip(conf_filename, logging_config, create_backup=create_backup) return @@ -269,7 +270,7 @@ def load_logging_config_for_edit(self): If logging.yaml does not contain a 'shng_version' key, a backup is created """ #self.etc_dir = self._sh.get_etcdir() - conf_filename = os.path.join(self._sh.get_etcdir(), 'logging') + conf_filename = os.path.join(self._sh.get_etcdir(), BASE_LOG) logging_config = shyaml.yaml_load_roundtrip(conf_filename) self.logger.info("load_logging_config_for_edit: shng_version={}".format(logging_config.get('shng_version', None))) diff --git a/lib/logic.py b/lib/logic.py index 1179237c05..892042539f 100644 --- a/lib/logic.py +++ b/lib/logic.py @@ -59,7 +59,7 @@ from lib.utils import Utils from lib.constants import PLUGIN_PARSE_LOGIC -from lib.constants import (YAML_FILE, CONF_FILE) +from lib.constants import (YAML_FILE, CONF_FILE, DIR_LOGICS, BASE_LOGIC) from lib.item import Items from lib.plugin import Plugins @@ -508,7 +508,7 @@ def is_userlogic(self, name): pathname = str(self.return_logic(name)._pathname) except: return False - return os.path.basename(os.path.dirname(pathname)) == 'logics' + return os.path.basename(os.path.dirname(pathname)) == DIR_LOGICS def load_logic(self, name): @@ -618,7 +618,7 @@ def return_logictype(self, name): else: logger.info("return_logictype: name {} is not loaded".format(name)) # load /etc/logic.yaml if logic is not in the loaded logics - conf_filename = os.path.join(self._get_etc_dir(), 'logic') + conf_filename = os.path.join(self._get_etc_dir(), BASE_LOGIC) config = shyaml.yaml_load_roundtrip(conf_filename) if config is not None: if name in config: @@ -654,7 +654,7 @@ def return_defined_logics(self, withtype=False): logic_list = [] # load /etc/logic.yaml - conf_filename = os.path.join(self._get_etc_dir(), 'logic') + conf_filename = os.path.join(self._get_etc_dir(), BASE_LOGIC) config = shyaml.yaml_load_roundtrip(conf_filename) if config is not None: @@ -729,7 +729,7 @@ def read_config_section(self, section): return False # load /etc/logic.yaml - conf_filename = os.path.join(self._get_etc_dir(), 'logic') + conf_filename = os.path.join(self._get_etc_dir(), BASE_LOGIC) _conf = shyaml.yaml_load_roundtrip(conf_filename) config_list = [] @@ -780,7 +780,7 @@ def set_config_section_key(self, section, key, value): """ # load /etc/logic.yaml - conf_filename = os.path.join(self._get_etc_dir(), 'logic') + conf_filename = os.path.join(self._get_etc_dir(), BASE_LOGIC) conf = shyaml.yaml_load_roundtrip(conf_filename) logger.info("set_config_section_key: section={}, key={}, value={}".format(section, key, str(value))) @@ -828,7 +828,7 @@ def update_config_section(self, active, section, config_list): return False # load /etc/logic.yaml - conf_filename = os.path.join(self._get_etc_dir(), 'logic') + conf_filename = os.path.join(self._get_etc_dir(), BASE_LOGIC) conf = shyaml.yaml_load_roundtrip(conf_filename) if conf is None: conf = shyaml.get_emptynode() @@ -908,7 +908,7 @@ def _count_filename_uses(self, conf, filename): def filename_used_count(self, filename): # load /etc/logic.yaml - conf_filename = os.path.join(self._get_etc_dir(), 'logic') + conf_filename = os.path.join(self._get_etc_dir(), BASE_LOGIC) conf = shyaml.yaml_load_roundtrip(conf_filename) count = self._count_filename_uses(conf, filename) return count @@ -939,7 +939,7 @@ def delete_logic(self, name, with_code=False): self.unload_logic(name) # load /etc/logic.yaml - conf_filename = os.path.join(self._get_etc_dir(), 'logic') + conf_filename = os.path.join(self._get_etc_dir(), BASE_LOGIC) conf = shyaml.yaml_load_roundtrip(conf_filename) section = conf.get(name, None) diff --git a/lib/metadata.py b/lib/metadata.py index 041f6cb113..2fd5a68d0e 100644 --- a/lib/metadata.py +++ b/lib/metadata.py @@ -27,7 +27,7 @@ from lib.utils import Utils from lib.utils import Version import lib.shyaml as shyaml -from lib.constants import (YAML_FILE, FOO, META_DATA_TYPES, META_DATA_DEFAULTS) +from lib.constants import (YAML_FILE, FOO, META_DATA_TYPES, META_DATA_DEFAULTS, DIR_PLUGINS, DIR_MODULES) META_MODULE_PARAMETER_SECTION = 'parameters' META_PLUGIN_SECTION = 'plugin' @@ -80,9 +80,9 @@ def __init__(self, sh, addon_name, addon_type, classpath=''): # logger.warning(self._log_premsg+"classpath = '{}'".format( classpath ) ) if classpath == '': if addon_type == 'plugin': - addon_type_dir = 'plugins' + addon_type_dir = DIR_PLUGINS elif addon_type == 'module': - addon_type_dir = 'modules' + addon_type_dir = DIR_MODULES else: return self.relative_filename = os.path.join( addon_type_dir, self._addon_name, addon_type+YAML_FILE ) diff --git a/lib/module.py b/lib/module.py index 3ea7f3dbe2..42a848f9ac 100644 --- a/lib/module.py +++ b/lib/module.py @@ -48,7 +48,7 @@ import lib.config import lib.translation as translation -from lib.constants import (KEY_CLASS_NAME, KEY_CLASS_PATH, KEY_INSTANCE,CONF_FILE) +from lib.constants import (KEY_CLASS_NAME, KEY_CLASS_PATH, KEY_INSTANCE, CONF_FILE, DIR_MODULES) from lib.utils import Utils from lib.metadata import Metadata @@ -127,7 +127,7 @@ def _get_modulename_and_metadata(self, module, mod_conf): module_name = mod_conf.get('module_name','').lower() meta = None if module_name != '': - module_dir = os.path.join(self._basedir, 'modules', module_name) + module_dir = os.path.join(self._basedir, DIR_MODULES, module_name) if os.path.isdir(module_dir): meta = Metadata(self._sh, module_name, 'module') else: @@ -181,7 +181,7 @@ def _get_classname_and_classpath(self, mod_conf, module_name): try: classpath = mod_conf[KEY_CLASS_PATH] except: - classpath = 'modules.' + module_name + classpath = DIR_MODULES + '.' + module_name return (classname, classpath) diff --git a/lib/plugin.py b/lib/plugin.py index bf586d2f08..b3f7494c09 100644 --- a/lib/plugin.py +++ b/lib/plugin.py @@ -59,7 +59,7 @@ import lib.translation as translation import lib.shyaml as shyaml from lib.model.smartplugin import SmartPlugin -from lib.constants import (KEY_CLASS_NAME, KEY_CLASS_PATH, KEY_INSTANCE, KEY_WEBIF_PAGELENGTH, YAML_FILE,CONF_FILE) +from lib.constants import (KEY_CLASS_NAME, KEY_CLASS_PATH, KEY_INSTANCE, KEY_WEBIF_PAGELENGTH, YAML_FILE, CONF_FILE, DIR_PLUGINS) #from lib.utils import Utils from lib.metadata import Metadata #import lib.item @@ -103,7 +103,7 @@ def __init__(self, smarthome, configfile): _plugins_instance = self # until Backend plugin is modified - if os.path.isfile(configfile+ YAML_FILE): + if os.path.isfile(configfile + YAML_FILE): self._plugin_conf_filename = configfile + YAML_FILE else: self._plugin_conf_filename = configfile + CONF_FILE @@ -323,7 +323,7 @@ def _get_classname_and_classpath(self, plg_conf, plugin_name): if plugin_name == '': classpath = '' else: - classpath = 'plugins.' + plugin_name + classpath = DIR_PLUGINS + '.' + plugin_name # logger.warning("_get_classname_and_classpath: plugin_name = {}, classpath = {}, classname = {}".format(plugin_name, classpath, classname)) return (classname, classpath+plugin_version) diff --git a/lib/scene.py b/lib/scene.py index a0708ca588..0cc4409645 100644 --- a/lib/scene.py +++ b/lib/scene.py @@ -36,6 +36,7 @@ from lib.utils import Utils from lib.shtime import Shtime import lib.shyaml as yaml +from lib.constants import YAML_FILE logger = logging.getLogger(__name__) @@ -92,7 +93,7 @@ def _load_scenes(self): if item.type() == 'scene': self.scene_file = os.path.join(self._scenes_dir, item.property.path) - scene_file_yaml = yaml.yaml_load(self.scene_file+'.yaml', ordered=False, ignore_notfound=True) + scene_file_yaml = yaml.yaml_load(self.scene_file + YAML_FILE, ordered=False, ignore_notfound=True) if scene_file_yaml is not None: # Reading yaml file with scene definition for state in scene_file_yaml: @@ -186,7 +187,7 @@ def _save_learned_values(self, scene): logger.debug(" - Saving value {} for state/ditem {}".format(lvalue, fkey)) logger.info(" -> to dict learned_dict {}:".format(learned_dict)) scene_learnfile = os.path.join(self._scenes_dir, scene+'_learned') - yaml.yaml_save(scene_learnfile+'.yaml', learned_dict) + yaml.yaml_save(scene_learnfile + YAML_FILE, learned_dict) return @@ -194,8 +195,8 @@ def _load_learned_values(self, scene): """ Load learned values for the scene from a file """ - scene_learnfile = os.path.join(self._scenes_dir, scene+'_learned') - learned_dict = yaml.yaml_load(scene_learnfile+'.yaml', ordered=False, ignore_notfound=True) + scene_learnfile = os.path.join(self._scenes_dir, scene + '_learned') + learned_dict = yaml.yaml_load(scene_learnfile + YAML_FILE, ordered=False, ignore_notfound=True) logger.info("Loading learned values for scene {} from file {}:".format(scene, scene_learnfile)) logger.info(" -> loaded dict learned_dict {}:".format(learned_dict)) if learned_dict is not None: diff --git a/lib/shtime.py b/lib/shtime.py index 2e8f5dedc0..6da0d58709 100644 --- a/lib/shtime.py +++ b/lib/shtime.py @@ -42,7 +42,7 @@ import os import lib.shyaml as shyaml -from lib.constants import (YAML_FILE) +from lib.constants import (YAML_FILE, BASE_HOLIDAY) #from lib.translation import translate from lib.translation import translate as lib_translate @@ -1141,7 +1141,7 @@ def _initialize_holidays(self): if self.holidays is None: self._etc_dir = self._sh._etc_dir - conf_filename = os.path.join(self._sh._etc_dir, 'holidays'+YAML_FILE) + conf_filename = os.path.join(self._sh._etc_dir, BASE_HOLIDAY + YAML_FILE) self.config = shyaml.yaml_load(conf_filename) location = self.config.get('location', None) diff --git a/lib/smarthome.py b/lib/smarthome.py index 0f2701f76b..cfc7347cde 100644 --- a/lib/smarthome.py +++ b/lib/smarthome.py @@ -98,7 +98,7 @@ import lib.shyaml from lib.shpypi import Shpypi from lib.triggertimes import TriggerTimes -from lib.constants import (YAML_FILE, CONF_FILE, DEFAULT_FILE) +from lib.constants import (YAML_FILE, CONF_FILE, DEFAULT_FILE, BASE_LOG, BASE_LOGIC, BASE_MODULE, BASE_PLUGIN, BASE_SH, DIR_CACHE, DIR_ENV, DIR_ETC, DIR_ITEMS, DIR_LIB, DIR_LOGICS, DIR_PLUGINS, DIR_SCENES, DIR_STRUCTS, DIR_TPL, DIR_UF, DIR_VAR) import lib.userfunctions as uf from lib.systeminfo import Systeminfo @@ -153,7 +153,7 @@ def initialize_dir_vars(self): self._base_dir = BASE self.base_dir = self._base_dir # **base_dir** is deprecated. Use method get_basedir() instead. - for external modules using that var (backend, ...?) - self._etc_dir = os.path.join(self._extern_conf_dir, 'etc') + self._etc_dir = os.path.join(self._extern_conf_dir, DIR_ETC) # self._conf_dir contains the base dir for config folders if self._config_etc: @@ -162,29 +162,29 @@ def initialize_dir_vars(self): self._conf_dir = self._extern_conf_dir # shng system dirs - self._var_dir = os.path.join(self._base_dir, 'var') - self._lib_dir = os.path.join(self._base_dir, 'lib') - self._plugins_dir = os.path.join(self._base_dir, 'plugins') + self._var_dir = os.path.join(self._base_dir, DIR_VAR) + self._lib_dir = os.path.join(self._base_dir, DIR_LIB) + self._plugins_dir = os.path.join(self._base_dir, DIR_PLUGINS) # env and var dirs - self._env_dir = os.path.join(self._lib_dir, 'env' + os.path.sep) - self._template_dir = os.path.join(self._base_dir, 'templates') - self._env_logic_conf_basename = os.path.join(self._env_dir, 'logic') - self._cache_dir = os.path.join(self._var_dir, 'cache' + os.path.sep) + self._env_dir = os.path.join(self._lib_dir, DIR_ENV + os.path.sep) + self._template_dir = os.path.join(self._base_dir, DIR_TPL) + self._cache_dir = os.path.join(self._var_dir, DIR_CACHE + os.path.sep) # user config dirs - self._items_dir = os.path.join(self._conf_dir, 'items' + os.path.sep) - self._structs_dir = os.path.join(self._conf_dir, 'structs') - self._logic_dir = os.path.join(self._conf_dir, 'logics' + os.path.sep) - self._functions_dir = os.path.join(self._conf_dir, 'functions' + os.path.sep) - self._scenes_dir = os.path.join(self._conf_dir, 'scenes' + os.path.sep) + self._items_dir = os.path.join(self._conf_dir, DIR_ITEMS + os.path.sep) + self._structs_dir = os.path.join(self._conf_dir, DIR_STRUCTS) + self._logic_dir = os.path.join(self._conf_dir, DIR_LOGICS + os.path.sep) + self._functions_dir = os.path.join(self._conf_dir, DIR_UF + os.path.sep) + self._scenes_dir = os.path.join(self._conf_dir, DIR_SCENES + os.path.sep) # system config files - self._smarthome_conf_basename = os.path.join(self._etc_dir, 'smarthome') - self._log_conf_basename = os.path.join(self._etc_dir, 'logging') - self._module_conf_basename = os.path.join(self._etc_dir, 'module') - self._plugin_conf_basename = os.path.join(self._etc_dir, 'plugin') - self._logic_conf_basename = os.path.join(self._etc_dir, 'logic') + self._smarthome_conf_basename = os.path.join(self._etc_dir, BASE_SH) + self._log_conf_basename = os.path.join(self._etc_dir, BASE_LOG) + self._module_conf_basename = os.path.join(self._etc_dir, BASE_MODULE) + self._plugin_conf_basename = os.path.join(self._etc_dir, BASE_PLUGIN) + self._logic_conf_basename = os.path.join(self._etc_dir, BASE_LOGIC) + self._env_logic_conf_basename = os.path.join(self._env_dir, BASE_LOGIC) def create_directories(self): diff --git a/lib/userfunctions.py b/lib/userfunctions.py index 6f410083e9..2cc0ed56f2 100644 --- a/lib/userfunctions.py +++ b/lib/userfunctions.py @@ -27,11 +27,12 @@ import logging from lib.translation import translate +from lib.constants import DIR_UF _logger = logging.getLogger(__name__) -_uf_subdir = 'functions' +_uf_subdir = DIR_UF _func_dir = None _sh = None From 5217ca68f67eed7f121d8d8c25369c37947f6b2a Mon Sep 17 00:00:00 2001 From: Morg42 <43153739+Morg42@users.noreply.github.com> Date: Tue, 23 Jul 2024 17:54:23 +0200 Subject: [PATCH 4/8] lib.smarthome: add generic get conffile/confdir method --- lib/constants.py | 3 ++- lib/smarthome.py | 50 ++++++++++++++++++++++++++++++++++-------------- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/lib/constants.py b/lib/constants.py index 8c60cefe7b..11c6c358a3 100644 --- a/lib/constants.py +++ b/lib/constants.py @@ -112,9 +112,10 @@ BASE_LOGIC = 'logic' BASE_STRUCT = 'struct' BASE_HOLIDAY = 'holidays' +BASE_ADMIN = 'admin' DIRS = (DIR_VAR, DIR_LIB, DIR_CACHE, DIR_ENV, DIR_TPL, DIR_PLUGINS, DIR_MODULES, DIR_ETC, DIR_ITEMS, DIR_STRUCTS, DIR_LOGICS, DIR_UF, DIR_SCENES) -BASES = (BASE_SH, BASE_LOG, BASE_MODULE, BASE_PLUGIN, BASE_LOGIC) +BASES = (BASE_SH, BASE_LOG, BASE_MODULE, BASE_PLUGIN, BASE_LOGIC, BASE_STRUCT, BASE_HOLIDAY, BASE_ADMIN) FILES = DIRS + BASES #attributes for 'autotimer' parameter diff --git a/lib/smarthome.py b/lib/smarthome.py index cfc7347cde..6bbf255ec1 100644 --- a/lib/smarthome.py +++ b/lib/smarthome.py @@ -98,7 +98,7 @@ import lib.shyaml from lib.shpypi import Shpypi from lib.triggertimes import TriggerTimes -from lib.constants import (YAML_FILE, CONF_FILE, DEFAULT_FILE, BASE_LOG, BASE_LOGIC, BASE_MODULE, BASE_PLUGIN, BASE_SH, DIR_CACHE, DIR_ENV, DIR_ETC, DIR_ITEMS, DIR_LIB, DIR_LOGICS, DIR_PLUGINS, DIR_SCENES, DIR_STRUCTS, DIR_TPL, DIR_UF, DIR_VAR) +from lib.constants import (YAML_FILE, CONF_FILE, DEFAULT_FILE, DIRS, BASES, BASE_LOG, BASE_LOGIC, BASE_MODULE, BASE_PLUGIN, BASE_SH, DIR_CACHE, DIR_ENV, DIR_ETC, DIR_ITEMS, DIR_LIB, DIR_LOGICS, DIR_MODULES, DIR_PLUGINS, DIR_SCENES, DIR_STRUCTS, DIR_TPL, DIR_UF, DIR_VAR) import lib.userfunctions as uf from lib.systeminfo import Systeminfo @@ -165,6 +165,7 @@ def initialize_dir_vars(self): self._var_dir = os.path.join(self._base_dir, DIR_VAR) self._lib_dir = os.path.join(self._base_dir, DIR_LIB) self._plugins_dir = os.path.join(self._base_dir, DIR_PLUGINS) + self._modules_dir = os.path.join(self._base_dir, DIR_MODULES) # env and var dirs self._env_dir = os.path.join(self._lib_dir, DIR_ENV + os.path.sep) @@ -535,33 +536,54 @@ def get_structsdir(self) -> str: """ return self._structs_dir - - def get_logicsdir(self) -> str: + def get_vardir(self): """ - Function to return the logics config directory + Function to return the var directory used by SmartHomeNG - :return: Config directory as an absolute path + :return: var directory as an absolute path + :rtype: str """ - return self._logic_dir + return self._var_dir - def get_functionsdir(self) -> str: + def get_config_dir(self, config): """ - Function to return the userfunctions config directory + Function to return a config dir used by SmartHomeNG + replace / prevent a plethora of get_dir() functions - :return: Config directory as an absolute path + Returns '' for invalid config strings. + + :return: directory as an absolute path + :rtype: str """ - return self._functions_dir + # method would work fine without this check, but... + # make sure we don't allow "any" function call here + if config not in DIRS: + return '' + if hasattr(self, f'get_{config}dir'): + return getattr(self, f'get_{config}dir')() + elif hasattr(self, f'_{config}_dir'): + return getattr(self, f'_{config}_dir') + + return '' - def get_vardir(self): + + def get_config_file(self, config, extension=YAML_FILE): """ - Function to return the var directory used by SmartHomeNG + Function to return a config file used by SmartHomeNG - :return: var directory as an absolute path + Returns '' for invalid config strings. + + :return: file name as an absolute path :rtype: str """ - return self._var_dir + # method would work fine without this check, but... + # make sure we don't allow "any" function call here + if config not in BASES: + return '' + + return os.path.join(self.get_etcdir(), config + extension) def getBaseDir(self): From 54dc46fde4adb4a38fe1aa7b37f2e9283c413fa4 Mon Sep 17 00:00:00 2001 From: Morg42 <43153739+Morg42@users.noreply.github.com> Date: Tue, 23 Jul 2024 19:04:41 +0200 Subject: [PATCH 5/8] lib.logic: introduce class-wide logic conf filename --- lib/logic.py | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/lib/logic.py b/lib/logic.py index 892042539f..c687c51119 100644 --- a/lib/logic.py +++ b/lib/logic.py @@ -59,7 +59,7 @@ from lib.utils import Utils from lib.constants import PLUGIN_PARSE_LOGIC -from lib.constants import (YAML_FILE, CONF_FILE, DIR_LOGICS, BASE_LOGIC) +from lib.constants import (YAML_FILE, CONF_FILE, DIR_LOGICS, DIR_ETC, BASE_LOGIC, BASE_ADMIN) from lib.item import Items from lib.plugin import Plugins @@ -97,8 +97,9 @@ def __init__(self, smarthome, userlogicconf, envlogicconf): self._userlogicconf = userlogicconf self._env_dir = smarthome._env_dir self._envlogicconf = envlogicconf - self._etc_dir = smarthome.get_etcdir() - self._logic_dir = smarthome.get_logicsdir() + self._etc_dir = smarthome.get_config_dir(DIR_ETC) + self._logic_dir = smarthome.get_config_dir(DIR_LOGICS) + self._logic_conf = smarthome.get_config_file(BASE_LOGIC) self._workers = [] self._logics = {} #self._bytecode = {} @@ -126,7 +127,7 @@ def __init__(self, smarthome, userlogicconf, envlogicconf): self._load_logic(name, _config) # load /etc/admin.yaml - admconf_filename = os.path.join(self._get_etc_dir(), 'admin') + admconf_filename = self._sh.get_config_file(BASE_ADMIN) _admin_conf = shyaml.yaml_load_roundtrip(admconf_filename) if _admin_conf.get('logics', None) is None: self._groups = {} @@ -137,7 +138,7 @@ def __init__(self, smarthome, userlogicconf, envlogicconf): def _save_groups(self): # load /etc/admin.yaml - admconf_filename = os.path.join(self._get_etc_dir(), 'admin') + admconf_filename = self._sh.get_config_file(BASE_ADMIN) _admin_conf = shyaml.yaml_load_roundtrip(admconf_filename) _admin_conf['logics']['groups'] = self._groups shyaml.yaml_save_roundtrip(admconf_filename, _admin_conf, create_backup=True) @@ -618,8 +619,7 @@ def return_logictype(self, name): else: logger.info("return_logictype: name {} is not loaded".format(name)) # load /etc/logic.yaml if logic is not in the loaded logics - conf_filename = os.path.join(self._get_etc_dir(), BASE_LOGIC) - config = shyaml.yaml_load_roundtrip(conf_filename) + config = shyaml.yaml_load_roundtrip(self._logic_conf) if config is not None: if name in config: filename = config[name].get('filename', '') @@ -654,8 +654,7 @@ def return_defined_logics(self, withtype=False): logic_list = [] # load /etc/logic.yaml - conf_filename = os.path.join(self._get_etc_dir(), BASE_LOGIC) - config = shyaml.yaml_load_roundtrip(conf_filename) + config = shyaml.yaml_load_roundtrip(self._logic_conf) if config is not None: for section in config: @@ -729,8 +728,7 @@ def read_config_section(self, section): return False # load /etc/logic.yaml - conf_filename = os.path.join(self._get_etc_dir(), BASE_LOGIC) - _conf = shyaml.yaml_load_roundtrip(conf_filename) + _conf = shyaml.yaml_load_roundtrip(self._logic_conf) config_list = [] if _conf is not None: @@ -780,8 +778,7 @@ def set_config_section_key(self, section, key, value): """ # load /etc/logic.yaml - conf_filename = os.path.join(self._get_etc_dir(), BASE_LOGIC) - conf = shyaml.yaml_load_roundtrip(conf_filename) + conf = shyaml.yaml_load_roundtrip(self._logic_conf) logger.info("set_config_section_key: section={}, key={}, value={}".format(section, key, str(value))) if value == None: @@ -791,7 +788,7 @@ def set_config_section_key(self, section, key, value): conf[section][key] = value # save /etc/logic.yaml - shyaml.yaml_save_roundtrip(conf_filename, conf, True) + shyaml.yaml_save_roundtrip(self._logic_conf, conf, True) # activate visu_acl without reloading the logic if key == 'visu_acl': @@ -828,8 +825,7 @@ def update_config_section(self, active, section, config_list): return False # load /etc/logic.yaml - conf_filename = os.path.join(self._get_etc_dir(), BASE_LOGIC) - conf = shyaml.yaml_load_roundtrip(conf_filename) + conf = shyaml.yaml_load_roundtrip(self._logic_conf) if conf is None: conf = shyaml.get_emptynode() @@ -889,7 +885,7 @@ def update_config_section(self, active, section, config_list): if conf[section] == shyaml.get_emptynode(): conf[section] = None - shyaml.yaml_save_roundtrip(conf_filename, conf, True) + shyaml.yaml_save_roundtrip(self._logic_conf, conf, True) def _count_filename_uses(self, conf, filename): @@ -908,8 +904,7 @@ def _count_filename_uses(self, conf, filename): def filename_used_count(self, filename): # load /etc/logic.yaml - conf_filename = os.path.join(self._get_etc_dir(), BASE_LOGIC) - conf = shyaml.yaml_load_roundtrip(conf_filename) + conf = shyaml.yaml_load_roundtrip(self._logic_conf) count = self._count_filename_uses(conf, filename) return count @@ -939,8 +934,7 @@ def delete_logic(self, name, with_code=False): self.unload_logic(name) # load /etc/logic.yaml - conf_filename = os.path.join(self._get_etc_dir(), BASE_LOGIC) - conf = shyaml.yaml_load_roundtrip(conf_filename) + conf = shyaml.yaml_load_roundtrip(self._logic_conf) section = conf.get(name, None) if section is None: @@ -974,7 +968,7 @@ def delete_logic(self, name, with_code=False): logger.info(f"delete_logic: Section '{name}' from configuration deleted") # save /etc/logic.yaml - shyaml.yaml_save_roundtrip(conf_filename, conf, True) + shyaml.yaml_save_roundtrip(self._logic_conf, conf, True) return True From 7e46c15a40c3f6bbe061accc150b535d09ec9078 Mon Sep 17 00:00:00 2001 From: Morg42 <43153739+Morg42@users.noreply.github.com> Date: Tue, 23 Jul 2024 19:05:05 +0200 Subject: [PATCH 6/8] core: move to get_config_dir/get_config_file --- lib/backup.py | 3 +++ lib/item/structs.py | 16 ++++++------- lib/log.py | 23 +++++++++++-------- lib/module.py | 4 ++-- lib/scene.py | 4 ++-- lib/shtime.py | 4 +--- lib/smarthome.py | 2 ++ lib/userfunctions.py | 2 +- modules/admin/api_auth.py | 5 ++-- modules/admin/api_config.py | 21 +++++++++-------- modules/admin/api_files.py | 27 +++++++++++----------- modules/admin/api_logics.py | 20 ++++++++-------- modules/admin/api_logs.py | 6 ++--- modules/admin/api_plugin.py | 4 ++-- modules/admin/api_plugins.py | 14 ++++++------ modules/admin/api_server.py | 6 ++--- modules/admin/api_services.py | 6 ++--- modules/admin/api_system.py | 6 ++--- tests/mock/core.py | 43 ++++++++++++++++++++++++++++++++++- 19 files changed, 132 insertions(+), 84 deletions(-) diff --git a/lib/backup.py b/lib/backup.py index 81f08e2377..fbb95c2219 100644 --- a/lib/backup.py +++ b/lib/backup.py @@ -105,6 +105,9 @@ def create_backup(conf_base_dir, base_dir, filename_with_timestamp=False, before backup_filename += '_' + get_backupdate() + '_' + get_backuptime() backup_filename += '.zip' + # let backup.py create file/directory names the "old way", because this can + # be invoked by command line without a working SmartHome instance and so + # needs to create the paths for itself. Hope this always works... :) etc_dir = os.path.join(conf_base_dir, 'etc') if config_etc: diff --git a/lib/item/structs.py b/lib/item/structs.py index 71aad6c407..1abf76bccf 100644 --- a/lib/item/structs.py +++ b/lib/item/structs.py @@ -26,7 +26,7 @@ import lib.shyaml as shyaml from lib.config import sanitize_items -from lib.constants import (YAML_FILE, BASE_STRUCT, DIR_STRUCTS) +from lib.constants import (YAML_FILE, BASE_STRUCT, DIR_STRUCTS, DIR_ETC) logger = logging.getLogger(__name__) @@ -49,8 +49,8 @@ def __init__(self, smarthome): self.logger = logging.getLogger(__name__) self._sh = smarthome self.save_joined_structs = False - self.etc_dir = self._sh.get_etcdir() - self.structs_dir = self._sh.get_structsdir() + self.etc_dir = self._sh.get_config_dir(DIR_ETC) + self.structs_dir = self._sh.get_config_dir(DIR_STRUCTS) def return_struct_definitions(self, all=True): @@ -86,13 +86,13 @@ def load_definitions_from_etc(self): - structs are read in from ../etc/struct.yaml by this procedure - further structs are read in from ../etc/struct_.yaml by this procedure """ - self.load_struct_definitions_from_file(self.etc_dir, BASE_STRUCT + YAML_FILE, '') + self.load_struct_definitions_from_file(self._sh.get_config_dir(DIR_ETC), BASE_STRUCT + YAML_FILE, '') # look for further struct files file_list = os.listdir(self.etc_dir) for filename in file_list: if filename.startswith(BASE_STRUCT + '_') and filename.endswith(YAML_FILE): - key_prefix = 'my.' + filename[7:-5] + key_prefix = 'my.' + filename[len(BASE_STRUCT):-len(YAML_FILE)] self.load_struct_definitions_from_file(self.etc_dir, filename, key_prefix) return @@ -119,7 +119,7 @@ def migrate_to_structs_dir(self): fl = os.listdir(self.etc_dir) for filename in fl: if filename.startswith(BASE_STRUCT + '_') and filename.endswith(YAML_FILE): - dest_fn = filename[7:] + dest_fn = filename[len(BASE_STRUCT):] self.migrate_one_file(filename, dest_fn) filename = BASE_STRUCT + YAML_FILE @@ -136,7 +136,7 @@ def load_definitions_from_structs(self): - structs are read in from ../structs/.yaml by this procedure """ - self.load_struct_definitions_from_file(self.etc_dir, BASE_STRUCT + YAML_FILE, '') + self.load_struct_definitions_from_file(self._sh.get_config_dir(DIR_ETC), BASE_STRUCT + YAML_FILE, '') if os.path.isdir(self.structs_dir): # look for struct files file_list = os.listdir(self.structs_dir) @@ -145,7 +145,7 @@ def load_definitions_from_structs(self): if filename == 'global_structs.yaml': key_prefix = '' else: - key_prefix = 'my.' + filename[:-5] + key_prefix = 'my.' + filename[:-len(YAML_FILE)] self.load_struct_definitions_from_file(self.structs_dir, filename, key_prefix) else: self.logger.notice("../" + DIR_STRUCTS + " does not exist") diff --git a/lib/log.py b/lib/log.py index ee162f20b1..f200dfdb3d 100644 --- a/lib/log.py +++ b/lib/log.py @@ -34,7 +34,7 @@ import collections import lib.shyaml as shyaml -from lib.constants import YAML_FILE, DEFAULT_FILE, BASE_LOG +from lib.constants import YAML_FILE, DEFAULT_FILE, BASE_LOG, DIR_ETC, DIR_VAR logs_instance = None @@ -68,13 +68,15 @@ def __init__(self, sh): return - def configure_logging(self, config_filename=BASE_LOG + YAML_FILE): + def configure_logging(self, config_filename=''): + if not config_filename: + config_filename = self._sh.get_config_file(BASE_LOG) config_dict = self.load_logging_config(config_filename, ignore_notfound=True) if config_dict == None: print() - print(f"ERROR: Invalid logging configuration in file '{os.path.join(self._sh.get_etcdir(), config_filename)}'") + print(f"ERROR: Invalid logging configuration in file '{config_filename}'") print() exit(1) @@ -141,7 +143,7 @@ def configure_logging(self, config_filename=BASE_LOG + YAML_FILE): logging.config.dictConfig(config_dict) except Exception as e: print() - print(f"ERROR: dictConfig: Invalid logging configuration in file '{os.path.join(self._sh.get_etcdir(), config_filename)}'") + print(f"ERROR: dictConfig: Invalid logging configuration in file '{config_filename}'") print(f" Exception: {e}") print() return e @@ -238,13 +240,16 @@ def return_logs(self): # --------------------------------------------------------------------------- - def load_logging_config(self, filename=BASE_LOG, ignore_notfound=False): + def load_logging_config(self, filename='', ignore_notfound=False): """ Load config from logging.yaml to a dict If logging.yaml does not contain a 'shng_version' key, a backup is created """ - conf_filename = os.path.join(self._sh.get_etcdir(), filename) + if not filename: + conf_filename = self._sh.get_config_file(BASE_LOG) + else: + conf_filename = os.path.join(self._sh.get_config_dir(DIR_ETC), filename) if not conf_filename.endswith(YAML_FILE) and not conf_filename.endswith(DEFAULT_FILE): conf_filename += YAML_FILE result = shyaml.yaml_load(conf_filename, ignore_notfound) @@ -258,7 +263,7 @@ def save_logging_config(self, logging_config, create_backup=False): """ if logging_config is not None: logging_config['shng_version'] = self._sh.version.split('-')[0][1:] - conf_filename = os.path.join(self._sh.get_etcdir(), BASE_LOG) + conf_filename = self._sh.get_config_file(BASE_LOG) shyaml.yaml_save_roundtrip(conf_filename, logging_config, create_backup=create_backup) return @@ -270,7 +275,7 @@ def load_logging_config_for_edit(self): If logging.yaml does not contain a 'shng_version' key, a backup is created """ #self.etc_dir = self._sh.get_etcdir() - conf_filename = os.path.join(self._sh.get_etcdir(), BASE_LOG) + conf_filename = self._sh.get_config_file(BASE_LOG) logging_config = shyaml.yaml_load_roundtrip(conf_filename) self.logger.info("load_logging_config_for_edit: shng_version={}".format(logging_config.get('shng_version', None))) @@ -788,7 +793,7 @@ def __init__(self, logname='undefined', maxlen=35, level=logging.NOTSET, self._cache = cache self._maxlen = maxlen # save cache files in var/log/cache directory - cache_directory = os.path.join(logs_instance._sh.get_vardir(), 'log'+os.path.sep, 'cache'+os.path.sep) + cache_directory = os.path.join(logs_instance._sh.get_config_dir(DIR_VAR), 'log'+os.path.sep, 'cache'+os.path.sep) if cache is True: if not os.path.isdir(cache_directory): os.makedirs(cache_directory) diff --git a/lib/module.py b/lib/module.py index 42a848f9ac..74d9f5aa11 100644 --- a/lib/module.py +++ b/lib/module.py @@ -48,7 +48,7 @@ import lib.config import lib.translation as translation -from lib.constants import (KEY_CLASS_NAME, KEY_CLASS_PATH, KEY_INSTANCE, CONF_FILE, DIR_MODULES) +from lib.constants import (KEY_CLASS_NAME, KEY_CLASS_PATH, KEY_INSTANCE, DIR_MODULES) from lib.utils import Utils from lib.metadata import Metadata @@ -127,7 +127,7 @@ def _get_modulename_and_metadata(self, module, mod_conf): module_name = mod_conf.get('module_name','').lower() meta = None if module_name != '': - module_dir = os.path.join(self._basedir, DIR_MODULES, module_name) + module_dir = os.path.join(self._sh.get_config_dir(DIR_MODULES), module_name) if os.path.isdir(module_dir): meta = Metadata(self._sh, module_name, 'module') else: diff --git a/lib/scene.py b/lib/scene.py index 0cc4409645..d56e99f3a2 100644 --- a/lib/scene.py +++ b/lib/scene.py @@ -36,7 +36,7 @@ from lib.utils import Utils from lib.shtime import Shtime import lib.shyaml as yaml -from lib.constants import YAML_FILE +from lib.constants import (YAML_FILE, DIR_SCENES) logger = logging.getLogger(__name__) @@ -82,7 +82,7 @@ def _load_scenes(self): """ self._scenes = {} self._learned_values = {} - self._scenes_dir = self._sh._scenes_dir + self._scenes_dir = self._sh.get_config_dir(DIR_SCENES) if not os.path.isdir(self._scenes_dir): logger.warning(translate("Directory '{scenes_dir}' not found. Ignoring scenes.", {'scenes_dir': self._scenes_dir})) return diff --git a/lib/shtime.py b/lib/shtime.py index 6da0d58709..38f7df46c4 100644 --- a/lib/shtime.py +++ b/lib/shtime.py @@ -1140,9 +1140,7 @@ def _initialize_holidays(self): """ if self.holidays is None: - self._etc_dir = self._sh._etc_dir - conf_filename = os.path.join(self._sh._etc_dir, BASE_HOLIDAY + YAML_FILE) - self.config = shyaml.yaml_load(conf_filename) + self.config = shyaml.yaml_load(self._sh.get_config_file(BASE_HOLIDAY)) location = self.config.get('location', None) # prepopulate holidays for following years diff --git a/lib/smarthome.py b/lib/smarthome.py index 6bbf255ec1..534ec86fe7 100644 --- a/lib/smarthome.py +++ b/lib/smarthome.py @@ -176,6 +176,8 @@ def initialize_dir_vars(self): self._items_dir = os.path.join(self._conf_dir, DIR_ITEMS + os.path.sep) self._structs_dir = os.path.join(self._conf_dir, DIR_STRUCTS) self._logic_dir = os.path.join(self._conf_dir, DIR_LOGICS + os.path.sep) + # TODO: remove self._logic_dir later for uniformness (dirs with plural naming) + self._logics_dir = self._logic_dir self._functions_dir = os.path.join(self._conf_dir, DIR_UF + os.path.sep) self._scenes_dir = os.path.join(self._conf_dir, DIR_SCENES + os.path.sep) diff --git a/lib/userfunctions.py b/lib/userfunctions.py index 2cc0ed56f2..a3efa3429e 100644 --- a/lib/userfunctions.py +++ b/lib/userfunctions.py @@ -93,7 +93,7 @@ def init_lib(shng_base_dir=None, sh=None): base_dir = os.getcwd() if _sh: - _func_dir = _sh.get_functionsdir() + _func_dir = _sh.get_config_dir(DIR_UF) else: _func_dir = os.path.join(base_dir, _uf_subdir) diff --git a/modules/admin/api_auth.py b/modules/admin/api_auth.py index 39d0c7bd7f..34f1e98e80 100644 --- a/modules/admin/api_auth.py +++ b/modules/admin/api_auth.py @@ -31,6 +31,7 @@ # from lib.item import Items from lib.utils import Utils +from lib.constants import (DIR_ETC, DIR_MODULES) from .rest import RESTResource @@ -44,8 +45,8 @@ def __init__(self, module): self.base_dir = self._sh.get_basedir() self.logger = logging.getLogger(__name__.split('.')[0] + '.' + __name__.split('.')[1] + '.' + __name__.split('.')[2][4:]) - self.etc_dir = self._sh._etc_dir - self.modules_dir = os.path.join(self.base_dir, 'modules') + self.etc_dir = self._sh.get_config_dir(DIR_ETC) + self.modules_dir = self._sh.get_config_dir(DIR_MODULES) if self.module.rest_dispatch_force_exception: self.logger.notice(f"REST_dispatch_execute warnlevel is set to EXCEPTION") diff --git a/modules/admin/api_config.py b/modules/admin/api_config.py index 099694a071..f462e6ff5b 100644 --- a/modules/admin/api_config.py +++ b/modules/admin/api_config.py @@ -27,6 +27,7 @@ from lib.module import Modules import lib.shyaml as shyaml +from lib.constants import (DIR_ETC, DIR_MODULES, BASE_SH, BASE_HOLIDAY, BASE_MODULE) from .rest import RESTResource @@ -42,8 +43,8 @@ def __init__(self, module): self.base_dir = self._sh.get_basedir() self.logger = logging.getLogger(__name__.split('.')[0] + '.' + __name__.split('.')[1] + '.' + __name__.split('.')[2][4:]) - self.etc_dir = self._sh.get_etcdir() - self.modules_dir = os.path.join(self.base_dir, 'modules') + self.etc_dir = self._sh.get_config_dir(DIR_ETC) + self.modules_dir = self._sh.get_config_dir(DIR_MODULES) self.core_conf = shyaml.yaml_load(os.path.join(self.modules_dir, 'core', 'module.yaml')) self.http_conf = shyaml.yaml_load(os.path.join(self.modules_dir, 'http', 'module.yaml')) @@ -72,7 +73,7 @@ def update_configdict(self, config_dict, data, section='unknown'): # Read holidays # def read_holidays(self): - self.holidays_confdata = shyaml.yaml_load(os.path.join(self.etc_dir, 'holidays.yaml')) + self.holidays_confdata = shyaml.yaml_load(self._sh.get_config_file(BASE_HOLIDAYS)) if self.holidays_confdata.get('location', None) is not None: self.core_confdata['holidays_country'] = self.holidays_confdata['location'].get('country', '') self.core_confdata['holidays_province'] = self.holidays_confdata['location'].get('province', '') @@ -95,7 +96,7 @@ def read_holidays(self): # Update holidays # def update_holidays(self, data): - filename = os.path.join(self.etc_dir, 'holidays.yaml') + filename = self._sh.get_config_file(BASE_HOLIDAYS) self.holidays_confdata = shyaml.yaml_load_roundtrip(filename) self.logger.info("update_holidays: self.holidays_confdata = '{}'".format(self.holidays_confdata)) self.logger.info("update_holidays: data['common']['data'] = '{}'".format(data['common']['data'])) @@ -152,10 +153,10 @@ def read(self, id=None): """ self.logger.info("ConfigController.read(): config = {}".format(id)) - self.core_confdata = shyaml.yaml_load(os.path.join(self.etc_dir, 'smarthome.yaml')) + self.core_confdata = shyaml.yaml_load(self._sh.get_config_file(BASE_SH)) self.read_holidays() - self.module_confdata = shyaml.yaml_load(os.path.join(self.etc_dir, 'module.yaml')) + self.module_confdata = shyaml.yaml_load(self._sh.get_config_file(BASE_MODULE)) result = {} if (not id) or id == 'core': @@ -239,12 +240,12 @@ def update(self, id=None): self.update_holidays(data) # update etc/smarthome.yaml with data from admin frontend - self.core_confdata = shyaml.yaml_load_roundtrip(os.path.join(self.etc_dir, 'smarthome.yaml')) + self.core_confdata = shyaml.yaml_load_roundtrip(self._sh.get_config_file(BASE_SH)) self.update_configdict(self.core_confdata, data, 'common') - shyaml.yaml_save_roundtrip(os.path.join(self.etc_dir, 'smarthome.yaml'), self.core_confdata, create_backup=True) + shyaml.yaml_save_roundtrip(self._sh.get_config_file(BASE_SH), self.core_confdata, create_backup=True) # update etc/module.yaml with data from admin frontend - self.module_confdata = shyaml.yaml_load_roundtrip(os.path.join(self.etc_dir, 'module.yaml')) + self.module_confdata = shyaml.yaml_load_roundtrip(self._sh.get_config_file(BASE_MODULE)) self.update_configdict(self.module_confdata['http'], data, 'http') self.mod_http = Modules.get_instance().get_module('http') hashed_password = data.get('http', {}).get('data', {}).get('hashed_password', '') @@ -276,7 +277,7 @@ def update(self, id=None): self.module_confdata['mqtt'].pop('enabled', None) self.logger.info("Update: ['mqtt'] = {}".format(self.module_confdata['mqtt'])) self.logger.info("Update: - enabled = {}".format(self.module_confdata['mqtt'].get('enabled', None))) - shyaml.yaml_save_roundtrip(os.path.join(self.etc_dir, 'module.yaml'), self.module_confdata, create_backup=True) + shyaml.yaml_save_roundtrip(self._sh.get_config_file(BASE_MODULE), self.module_confdata, create_backup=True) result = {"result": "ok"} return json.dumps(result) diff --git a/modules/admin/api_files.py b/modules/admin/api_files.py index 79b2c4cc3c..1e2ddc2409 100644 --- a/modules/admin/api_files.py +++ b/modules/admin/api_files.py @@ -35,6 +35,7 @@ from lib.item_conversion import convert_yaml as convert_yaml from lib.item_conversion import parse_for_convert as parse_for_convert from lib.shtime import Shtime +from lib.constants import (DIR_ETC, DIR_ITEMS, DIR_UF, DIR_SCENES, DIR_LOGICS, DIR_TPL, DIR_MODULES, BASE_LOG, BASE_STRUCT) # ====================================================================== @@ -51,16 +52,14 @@ def __init__(self, module): self.base_dir = self._sh.get_basedir() self.logger = logging.getLogger(__name__.split('.')[0] + '.' + __name__.split('.')[1] + '.' + __name__.split('.')[2][4:]) - self.etc_dir = self._sh.get_etcdir() - self.items_dir = self._sh._items_dir - self.functions_dir = self._sh.get_functionsdir() - self.scenes_dir = self._sh._scenes_dir - self.logics_dir = self._sh.get_logicsdir() - self.template_dir = self._sh._template_dir - self.extern_conf_dir = self._sh._extern_conf_dir - self.modules_dir = os.path.join(self.base_dir, 'modules') - return - + self.etc_dir = self._sh.get_config_dir(DIR_ETC) + self.items_dir = self._sh.get_config_dir(DIR_ITEMS) + self.functions_dir = self._sh.get_config_dir(DIR_UF) + self.scenes_dir = self._sh.get_config_dir(DIR_SCENES) + self.logics_dir = self._sh.get_config_dir(DIR_LOGICS) + self.template_dir = self._sh.get_config_dir(DIR_TPL) + self.extern_conf_dir = self._sh.get_confdir() + self.modules_dir = self._sh.get_config_dir(DIR_MODULES) def get_body(self, text=False, binary=False): """ @@ -113,7 +112,7 @@ def get_body(self, text=False, binary=False): def get_logging_config(self): self.logger.info("FilesController.get_logging_config()") - filename = os.path.join(self.etc_dir, 'logging.yaml') + filename = self._sh.get_config_file(BASE_LOG) read_data = None with open(filename, encoding='UTF-8') as f: read_data = f.read() @@ -135,7 +134,7 @@ def save_logging_config(self): self.logger.debug("FilesController.save_logging_config(): '{}'".format(params)) - filename = os.path.join(self.etc_dir, 'logging.yaml') + filename = self._sh.get_config_file(BASE_LOG) read_data = None with open(filename, 'w', encoding='UTF-8') as f: f.write(params) @@ -150,7 +149,7 @@ def save_logging_config(self): def get_struct_config(self): self.logger.info("FilesController.get_struct_config()") - filename = os.path.join(self.etc_dir, 'struct.yaml') + filename = self._sh.get_config_file(BASE_STRUCT) if not(os.path.isfile(filename)): open(filename, 'a', encoding='UTF-8').close() self.logger.info("FilesController.get_struct_config(): created empty file {}".format(filename)) @@ -176,7 +175,7 @@ def save_struct_config(self): self.logger.debug("FilesController.save_struct_config(): '{}'".format(params)) - filename = os.path.join(self.etc_dir, 'struct.yaml') + filename = self._sh.get_config_file(BASE_STRUCT) read_data = None with open(filename, 'w', encoding='UTF-8') as f: f.write(params) diff --git a/modules/admin/api_logics.py b/modules/admin/api_logics.py index 6e7cb707d5..e31636346e 100644 --- a/modules/admin/api_logics.py +++ b/modules/admin/api_logics.py @@ -34,6 +34,8 @@ from lib.logic import Logics from lib.plugin import Plugins from lib.scheduler import Scheduler +from lib.constants import (DIR_ETC, DIR_LOGICS, DIR_TPL, BASE_LOGIC) + from .rest import RESTResource @@ -47,10 +49,10 @@ def __init__(self, module): self.base_dir = self._sh.get_basedir() self.logger = logging.getLogger(__name__.split('.')[0] + '.' + __name__.split('.')[1] + '.' + __name__.split('.')[2][4:]) - self.etc_dir = self._sh._etc_dir + self.etc_dir = self._sh.get_config_dir(DIR_ETC) - self.logics_dir = self._sh.get_logicsdir() - self.template_dir = self._sh._template_dir + self.logics_dir = self._sh.get_config_dir(DIR_LOGICS) + self.template_dir = self._sh.get_config_dir(DIR_TPL) self.logics = Logics.get_instance() self.logger.info("__init__ self.logics = {}".format(self.logics)) self.plugins = Plugins.get_instance() @@ -62,7 +64,6 @@ def __init__(self, module): self.logics_data = {} self.logics = Logics.get_instance() - return def get_body(self, text=False): @@ -117,7 +118,6 @@ def logics_initialize(self): self.blockly_plugin_loaded = True except: pass - return def fill_logicdict(self, logicname): """ @@ -283,8 +283,7 @@ def get_logic_info(self, logicname): """ Get code of a logic from file """ - config_filename = os.path.join(self.etc_dir, 'logic.yaml') - wrk = shyaml.yaml_load(config_filename) + wrk = shyaml.yaml_load(self._sh.get_config_file(BASE_LOGIC)) logic_conf = wrk.get(logicname, {}) if Utils.get_type(logic_conf.get('watch_item', None)) == 'str': @@ -413,7 +412,7 @@ def set_logic_state(self, logicname, action, filename): self.logger.warning("LogicsController.set_logic_state(create): Logic name {} is already used".format(logicname)) return json.dumps({"result": "error", "description": "Logic name {} is already used".format(logicname)}) else: - if not os.path.isfile(os.path.join(self.logics.get_logics_dir(), filename)): + if not os.path.isfile(os.path.join(self._sh.get_config_dir(DIR_LOGICS), filename)): #create new logic code file, if none is found logics_code = self.get_logic_template(filename) if not self.logic_create_codefile(filename, logics_code): @@ -440,8 +439,7 @@ def save_logic_parameters(self, logicname, params): #params = self.get_body() self.logger.info(f"LogicsController.save_logic_parameters: logic = {logicname}, params = {params}") - config_filename = os.path.join(self.etc_dir, 'logic') - logic_conf = shyaml.yaml_load_roundtrip(config_filename) + logic_conf = shyaml.yaml_load_roundtrip(self._sh.get_config_file(BASE_LOGIC)) sect = logic_conf.get(logicname) if sect is None: response = {'result': 'error', 'description': "Configuration section '{}' does not exist".format(logicname)} @@ -474,7 +472,7 @@ def save_logic_parameters(self, logicname, params): self.logger.info("LogicsController.save_logic_parameters: logic = {}, neue params = {}".format(logicname, dict(sect))) - shyaml.yaml_save_roundtrip(config_filename, logic_conf, False) + shyaml.yaml_save_roundtrip(self._sh.get_config_file(BASE_LOGIC), logic_conf, False) response = {'result': 'ok'} return json.dumps(response) diff --git a/modules/admin/api_logs.py b/modules/admin/api_logs.py index 934787639c..8189ac5027 100644 --- a/modules/admin/api_logs.py +++ b/modules/admin/api_logs.py @@ -27,7 +27,7 @@ import lib.shyaml as shyaml from lib.utils import Utils - +from lib.constants import (DIR_ETC, BASE_LOG) import jwt from .rest import RESTResource @@ -40,10 +40,10 @@ def __init__(self, module): self.base_dir = self._sh.get_basedir() self.logger = logging.getLogger(__name__.split('.')[0] + '.' + __name__.split('.')[1] + '.' + __name__.split('.')[2][4:]) - self.etc_dir = self._sh._etc_dir + self.etc_dir = self._sh.get_config_dir(DIR_ETC) self.log_dir = os.path.join(self.base_dir, 'var', 'log') - self.logging_conf = shyaml.yaml_load(os.path.join(self.etc_dir, 'logging.yaml')) + self.logging_conf = shyaml.yaml_load(self._sh.get_config_file(BASE_LOG)) self.chunksize = self.module.log_chunksize diff --git a/modules/admin/api_plugin.py b/modules/admin/api_plugin.py index 6ea940ae30..74587ee7ff 100644 --- a/modules/admin/api_plugin.py +++ b/modules/admin/api_plugin.py @@ -32,7 +32,7 @@ from lib.plugin import Plugins from lib.metadata import Metadata from lib.model.smartplugin import SmartPlugin -from lib.constants import (KEY_CLASS_PATH, YAML_FILE) +from lib.constants import (KEY_CLASS_PATH, YAML_FILE, DIR_PLUGINS) from .rest import RESTResource @@ -42,7 +42,7 @@ class PluginController(RESTResource): def __init__(self, module, jwt_secret=False): self._sh = module._sh self.base_dir = self._sh.get_basedir() - self.plugins_dir = os.path.join(self.base_dir, 'plugins') + self.plugins_dir = self._sh.get_config_dir(DIR_PLUGINS) self.logger = logging.getLogger(__name__.split('.')[0] + '.' + __name__.split('.')[1] + '.' + __name__.split('.')[2][4:]) self.logger.info("PluginController(): __init__") self.plugins = Plugins.get_instance() diff --git a/modules/admin/api_plugins.py b/modules/admin/api_plugins.py index dd7be5ab00..b644f6115e 100644 --- a/modules/admin/api_plugins.py +++ b/modules/admin/api_plugins.py @@ -36,7 +36,7 @@ from lib.plugin import Plugins from lib.metadata import Metadata from lib.model.smartplugin import SmartPlugin -from lib.constants import (KEY_CLASS_PATH, YAML_FILE) +from lib.constants import (KEY_CLASS_PATH, YAML_FILE, DIR_PLUGINS) from .rest import RESTResource @@ -45,7 +45,7 @@ class PluginsController(RESTResource): def __init__(self, module): self._sh = module._sh self.base_dir = self._sh.get_basedir() - self.plugins_dir = os.path.join(self.base_dir, 'plugins') + self.plugins_dir = self._sh.get_config_dir(DIR_PLUGINS) self.logger = logging.getLogger(__name__.split('.')[0] + '.' + __name__.split('.')[1] + '.' + __name__.split('.')[2][4:]) self.plugin_data = {} @@ -95,7 +95,7 @@ class PluginsInstalledController(RESTResource): def __init__(self, module): self._sh = module._sh self.base_dir = self._sh.get_basedir() - self.plugins_dir = os.path.join(self.base_dir, 'plugins') + self.plugins_dir = self._sh.get_config_dir(DIR_PLUGINS) self.logger = logging.getLogger(__name__.split('.')[0] + '.' + __name__.split('.')[1] + '.' + __name__.split('.')[2][4:]) self.plugin_data = {} @@ -164,7 +164,7 @@ class PluginsConfigController(RESTResource): def __init__(self, module): self._sh = module._sh self.base_dir = self._sh.get_basedir() - self.plugins_dir = os.path.join(self.base_dir, 'plugins') + self.plugins_dir = self._sh.get_config_dir(DIR_PLUGINS) self.logger = logging.getLogger(__name__.split('.')[0] + '.' + __name__.split('.')[1] + '.' + __name__.split('.')[2][4:]) self.plugins = Plugins.get_instance() @@ -282,7 +282,7 @@ def __init__(self, module, shng_url_root): self.shng_url_root = shng_url_root self.base_dir = self._sh.get_basedir() - self.plugins_dir = os.path.join(self.base_dir, 'plugins') + self.plugins_dir = self._sh.get_config_dir(DIR_PLUGINS) self.logger = logging.getLogger(__name__.split('.')[0] + '.' + __name__.split('.')[1] + '.' + __name__.split('.')[2][4:]) self.plugins = Plugins.get_instance() @@ -510,7 +510,7 @@ def __init__(self, module): self.module = module self.base_dir = self._sh.get_basedir() - self.plugins_dir = os.path.join(self.base_dir, 'plugins') + self.plugins_dir = self._sh.get_config_dir(DIR_PLUGINS) self.logger = logging.getLogger(__name__.split('.')[0] + '.' + __name__.split('.')[1] + '.' + __name__.split('.')[2][4:]) self.plugins = Plugins.get_instance() @@ -553,7 +553,7 @@ def __init__(self, module): self.module = module self.base_dir = self._sh.get_basedir() - self.plugins_dir = os.path.join(self.base_dir, 'plugins') + self.plugins_dir = self._sh.get_config_dir(DIR_PLUGINS) self.logger = logging.getLogger(__name__.split('.')[0] + '.' + __name__.split('.')[1] + '.' + __name__.split('.')[2][4:]) self.plugins = Plugins.get_instance() diff --git a/modules/admin/api_server.py b/modules/admin/api_server.py index fe8853d69a..62d1385c73 100644 --- a/modules/admin/api_server.py +++ b/modules/admin/api_server.py @@ -30,6 +30,7 @@ import bin.shngversion import lib.daemon import lib.backup as backup +from lib.constants import (DIR_ETC, DIR_MODULES) # ====================================================================== @@ -75,9 +76,8 @@ def __init__(self, module): self.base_dir = self._sh.get_basedir() self.logger = logging.getLogger(__name__.split('.')[0] + '.' + __name__.split('.')[1] + '.' + __name__.split('.')[2][4:]) - self.etc_dir = self._sh._etc_dir - self.modules_dir = os.path.join(self.base_dir, 'modules') - return + self.etc_dir = self._sh.get_config_dir(DIR_ETC) # not used? + self.modules_dir = self._sh.get_config_dir(DIR_ETC) # not used? # ====================================================================== diff --git a/modules/admin/api_services.py b/modules/admin/api_services.py index 8e671cabe5..639f5c7d11 100755 --- a/modules/admin/api_services.py +++ b/modules/admin/api_services.py @@ -35,6 +35,7 @@ from lib.item_conversion import parse_for_convert as parse_for_convert from lib.shtime import Shtime import lib.env +from lib.constants import (DIR_ETC, DIR_MODULES) # ====================================================================== @@ -51,9 +52,8 @@ def __init__(self, module): self.base_dir = self._sh.get_basedir() self.logger = logging.getLogger(__name__.split('.')[0] + '.' + __name__.split('.')[1] + '.' + __name__.split('.')[2][4:]) - self.etc_dir = self._sh._etc_dir - self.modules_dir = os.path.join(self.base_dir, 'modules') - return + self.etc_dir = self._sh.get_config_dir(DIR_ETC) # not used? + self.modules_dir = self._sh.get_config_dir(DIR_ETC) # not used? def get_body(self, text=False): diff --git a/modules/admin/api_system.py b/modules/admin/api_system.py index 481a0f0382..06ee77cbe6 100644 --- a/modules/admin/api_system.py +++ b/modules/admin/api_system.py @@ -42,6 +42,7 @@ from lib.shpypi import Shpypi from lib.shtime import Shtime from lib.utils import Utils +from lib.constants import (DIR_ETC, DIR_MODULES) # ====================================================================== @@ -87,9 +88,8 @@ def __init__(self, module): self.base_dir = self._sh.get_basedir() self.logger = logging.getLogger(__name__.split('.')[0] + '.' + __name__.split('.')[1] + '.' + __name__.split('.')[2][4:]) - self.etc_dir = self._sh._etc_dir - self.modules_dir = os.path.join(self.base_dir, 'modules') - return + self.etc_dir = self._sh.get_config_dir(DIR_ETC) # not used? + self.modules_dir = self._sh.get_config_dir(DIR_ETC) # not used? # ====================================================================== diff --git a/tests/mock/core.py b/tests/mock/core.py index 967d56c345..47e69d5079 100644 --- a/tests/mock/core.py +++ b/tests/mock/core.py @@ -16,7 +16,7 @@ from lib.module import Modules import lib.utils from lib.model.smartplugin import SmartPlugin -from lib.constants import (YAML_FILE, CONF_FILE, DEFAULT_FILE) +from lib.constants import (YAML_FILE, CONF_FILE, DEFAULT_FILE, BASES, DIRS) from tests.common import BASE @@ -80,6 +80,8 @@ class MockSmartHome(): # _items_dir = os.path.join(_base_dir, 'items'+os.path.sep) _logic_conf_basename = os.path.join(_etc_dir, 'logic') _logic_dir = os.path.join(_base_dir, 'tests', 'resources', 'logics'+os.path.sep) + # for now, later remove _logic_dir, see lib/smarthome.py + _logics_dir = _logic_dir # _cache_dir = os.path.join(_var_dir,'cache'+os.path.sep) _log_conf_basename = os.path.join(_etc_dir, 'logging') # _smarthome_conf_basename = None @@ -253,6 +255,45 @@ def getBaseDir(self): """ return self._base_dir + def get_config_dir(self, config): + """ + Function to return a config dir used by SmartHomeNG + replace / prevent a plethora of get_dir() functions + + Returns '' for invalid config strings. + + :return: directory as an absolute path + :rtype: str + """ + # method would work fine without this check, but... + # make sure we don't allow "any" function call here + if config not in DIRS: + return '' + + if hasattr(self, f'get_{config}dir'): + return getattr(self, f'get_{config}dir')() + elif hasattr(self, f'_{config}_dir'): + return getattr(self, f'_{config}_dir') + + return '' + + + def get_config_file(self, config, extension=YAML_FILE): + """ + Function to return a config file used by SmartHomeNG + + Returns '' for invalid config strings. + + :return: file name as an absolute path + :rtype: str + """ + # method would work fine without this check, but... + # make sure we don't allow "any" function call here + if config not in BASES: + return '' + + return os.path.join(self.get_etcdir(), config + extension) + def trigger(self, name, obj=None, by='Logic', source=None, value=None, dest=None, prio=3, dt=None): logger.warning('MockSmartHome (trigger): {}'.format(str(obj))) From bafb713e997fcd9a761220cab64c11a8577b7176 Mon Sep 17 00:00:00 2001 From: Morg42 <43153739+Morg42@users.noreply.github.com> Date: Fri, 26 Jul 2024 22:58:47 +0200 Subject: [PATCH 7/8] core: rename templates_dir var --- lib/smarthome.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/smarthome.py b/lib/smarthome.py index 534ec86fe7..fd2afe3ff1 100644 --- a/lib/smarthome.py +++ b/lib/smarthome.py @@ -166,10 +166,10 @@ def initialize_dir_vars(self): self._lib_dir = os.path.join(self._base_dir, DIR_LIB) self._plugins_dir = os.path.join(self._base_dir, DIR_PLUGINS) self._modules_dir = os.path.join(self._base_dir, DIR_MODULES) + self._templates_dir = os.path.join(self._base_dir, DIR_TPL) # env and var dirs self._env_dir = os.path.join(self._lib_dir, DIR_ENV + os.path.sep) - self._template_dir = os.path.join(self._base_dir, DIR_TPL) self._cache_dir = os.path.join(self._var_dir, DIR_CACHE + os.path.sep) # user config dirs From b4ccd9fae3049fdbef111d1db4e98e5152fde1f8 Mon Sep 17 00:00:00 2001 From: Morg42 <43153739+Morg42@users.noreply.github.com> Date: Fri, 26 Jul 2024 23:07:07 +0200 Subject: [PATCH 8/8] shng: modify gitignore structure --- .gitignore | 28 ++++++++++++++++++++++++++++ etc/.gitignore | 13 ------------- functions/.gitignore | 5 ----- functions/uf.tpl | 25 ------------------------- items/.gitignore | 4 ---- logics/.gitignore | 6 ------ logics/logic.tpl | 19 ------------------- scenes/.gitignore | 4 ---- structs/.gitignore | 4 ---- 9 files changed, 28 insertions(+), 80 deletions(-) delete mode 100644 etc/.gitignore delete mode 100644 functions/.gitignore delete mode 100644 functions/uf.tpl delete mode 100644 items/.gitignore delete mode 100644 logics/.gitignore delete mode 100644 logics/logic.tpl delete mode 100644 scenes/.gitignore delete mode 100644 structs/.gitignore diff --git a/.gitignore b/.gitignore index c7ee02c58f..c87a1fe575 100644 --- a/.gitignore +++ b/.gitignore @@ -65,3 +65,31 @@ requirements/conf_all.txt **/.vscode/ /custom_plugins /priv_tools + + +# ./etc -- ignore everything +etc/* +# except yaml default-files +!etc/admin.yaml.default +!etc/holidays.yaml.default +!etc/logging.yaml.default +!etc/logic.yaml.default +!etc/module.yaml.default +!etc/plugin.yaml.default +!etc/smarthome.yaml.default + +# ./items -- ignore everything +items/ + +# ./logics -- ignore everything +logics/ + +# ./functions -- ignore everything +functions/ + +# ./structs -- ignore everything +structs/ + +# ./scenes -- ignore everything +scenes/ + diff --git a/etc/.gitignore b/etc/.gitignore deleted file mode 100644 index f29354a153..0000000000 --- a/etc/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -# ignore everything -* -# except .gitignore -!.gitignore -#!logging.yaml -# except yaml default-files -!admin.yaml.default -!holidays.yaml.default -!logging.yaml.default -!logic.yaml.default -!module.yaml.default -!plugin.yaml.default -!smarthome.yaml.default diff --git a/functions/.gitignore b/functions/.gitignore deleted file mode 100644 index 5b5726d9f4..0000000000 --- a/functions/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# ignore everything -* -# except .gitignore and template file -!.gitignore -!uf.tpl diff --git a/functions/uf.tpl b/functions/uf.tpl deleted file mode 100644 index dd523d0aad..0000000000 --- a/functions/uf.tpl +++ /dev/null @@ -1,25 +0,0 @@ -# -# This file contains user defined functions for use with SmartHomeNG -# -import logging -_logger = logging.getLogger(__name__) - -_VERSION = '0.1.0' -_DESCRIPTION = 'Per Anhalter durch die Galaxis' - -# -# Example functions -# -def zweiundvierzig(): - - return 'Die Antwort auf die Frage aller Fragen' - -def itemtest(sh): - - return sh.env.location.sun_position.elevation.degrees() - -def log_test(): - - _logger.warning('Log-Test aus einer Userfunction') - - return diff --git a/items/.gitignore b/items/.gitignore deleted file mode 100644 index 6c22af6ce7..0000000000 --- a/items/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# ignore everything -* -# except .gitignore -!.gitignore diff --git a/logics/.gitignore b/logics/.gitignore deleted file mode 100644 index f850825a77..0000000000 --- a/logics/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -# ignore everything -* -# except .gitignore -!.gitignore -!example_*.* -!logic.tpl diff --git a/logics/logic.tpl b/logics/logic.tpl deleted file mode 100644 index 280e34fa01..0000000000 --- a/logics/logic.tpl +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python3 -# -# This file contains a logic for use with SmartHomeNG -# -# Name of the logic: example_logic.py -# - -# This logic performs the following function(s): -# -# ... -# - -# The following triggers should be defined in ../etc/logic.yaml: -# -# watch_item = | | ... -# crontab = init = Init -# cycle = 600 -# - diff --git a/scenes/.gitignore b/scenes/.gitignore deleted file mode 100644 index 6c22af6ce7..0000000000 --- a/scenes/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# ignore everything -* -# except .gitignore -!.gitignore diff --git a/structs/.gitignore b/structs/.gitignore deleted file mode 100644 index 6c22af6ce7..0000000000 --- a/structs/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# ignore everything -* -# except .gitignore -!.gitignore