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/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/lib/backup.py b/lib/backup.py index a06b4fc7d4..b088572f91 100644 --- a/lib/backup.py +++ b/lib/backup.py @@ -23,16 +23,15 @@ This library creates a zip file with the configuration of SmartHomeNG. """ -import copy import glob import logging import zipfile -import shutil import time import os 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 +58,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: @@ -79,7 +78,6 @@ def make_backup_directories(base_dir): return True - def create_backup(conf_base_dir, base_dir, filename_with_timestamp=False, before_restore=False, config_etc=False): """ Create a zip file containing the configuration of SmartHomeNG. The zip file is stored in var/backup @@ -95,7 +93,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: @@ -104,6 +102,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: @@ -111,33 +112,30 @@ 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) esphome_conf_dir = os.path.join(conf_dir, 'var', 'esphome', 'config') # create new zip file - #backupzip = zipfile.ZipFile(backup_dir + os.path.sep + backup_filename, mode='w') backupzip = zipfile.ZipFile(backup_dir + os.path.sep + backup_filename, mode='w', compression=zipfile.ZIP_DEFLATED) - #backupzip = zipfile.ZipFile(backup_dir + os.path.sep + backup_filename, mode='w', compression=zipfile.ZIP_STORED) # backup files from /etc - #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) @@ -149,31 +147,31 @@ def create_backup(conf_base_dir, base_dir, filename_with_timestamp=False, before backup_directory(backupzip, etc_dir, '.key') # backup files from /items - #logger.warning("- items_dir = {}".format(items_dir)) + # logger.warning("- items_dir = {}".format(items_dir)) backup_directory(backupzip, items_dir) # backup files from /logic - #logger.warning("- logic_dir = {}".format(logic_dir)) + # logger.warning("- logic_dir = {}".format(logic_dir)) backup_directory(backupzip, logic_dir, '.py') backup_directory(backupzip, logic_dir, '.txt') # backup files from /scenes - #logger.warning("- scenes_dir = {}".format(scenes_dir)) - backup_directory(backupzip, scenes_dir, '.yaml') - backup_directory(backupzip, scenes_dir, '.conf') + # logger.warning("- scenes_dir = {}".format(scenes_dir)) + 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') + # logger.warning("- structs_dir = {}".format(structs_dir)) + backup_directory(backupzip, structs_dir, YAML_FILE) # backup files from /functions - #logger.warning("- uf_dir = {}".format(uf_dir)) + # logger.warning("- uf_dir = {}".format(uf_dir)) backup_directory(backupzip, uf_dir, '.*') # 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) # backup files from /var/esphome/config backup_directory(backupzip, esphome_conf_dir, '.yaml', 'esphome_config') @@ -190,7 +188,7 @@ def create_backup(conf_base_dir, base_dir, filename_with_timestamp=False, before logger.info("Zipped files: {}".format(zipped_files)) backupzip.close() - #logger.warning("- backup_dir = {}".format(backup_dir)) + # logger.warning("- backup_dir = {}".format(backup_dir)) shtime = Shtime.get_instance() if shtime == None: @@ -225,7 +223,6 @@ def get_lastbackuptime(): return "" - def backup_file(backupzip, source_dir, arc_dir, filename): """ Backup one file to a zip-archive @@ -237,33 +234,30 @@ def backup_file(backupzip, source_dir, arc_dir, filename): """ if not filename.startswith('.'): if os.path.isfile(os.path.join(source_dir, filename)): - #logger.debug("Zipping file: {}".format(filename)) + # logger.debug("Zipping file: {}".format(filename)) backupzip.write(os.path.join(source_dir, filename), arcname=os.path.join(arc_dir, filename)) return -def backup_directory(backupzip, source_dir, extenstion='.yaml', dest_dir=None): +def backup_directory(backupzip, source_dir, extension=YAML_FILE, dest_dir=None): """ 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) """ if os.path.isdir(source_dir): path = source_dir.split(os.path.sep) - dir = path[len(path)-1] + dir = path[len(path) - 1] if dest_dir is None: arc_dir = dir + os.path.sep else: arc_dir = dest_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 - def restore_backup(conf_base_dir, base_dir, config_etc=False): """ @@ -280,7 +274,7 @@ def restore_backup(conf_base_dir, base_dir, config_etc=False): :return: """ - logger.info(f"Beginning restore of configuration") + logger.info("Beginning restore of configuration") make_backup_directories(base_dir) restore_dir = os.path.join(base_dir, 'var', 'restore') @@ -292,11 +286,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) esphome_conf_dir = os.path.join(conf_dir, 'var', 'esphome', 'config') archive_file = '' @@ -317,39 +311,40 @@ 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") + logger.info("Creating a backup of the current configuration before restoring configuration from zip-file") create_backup(conf_base_dir, base_dir, True, True) overwrite = True # open existing zip file restorezip = zipfile.ZipFile(restorezip_filename, mode='r') - #restorezip = zipfile.ZipFile(restorezip_filename, mode='r', compression=zipfile.ZIP_STORED) + # 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) - # restore files to /logic - restore_directory(restorezip, 'logics', logic_dir, overwrite) + # backup files from /logic + restore_directory(restorezip, DIR_LOGICS, logic_dir, overwrite) - # restore files to /scenes - restore_directory(restorezip, 'scenes', scenes_dir, overwrite) + # backup files from /scenes + restore_directory(restorezip, DIR_SCENES, scenes_dir, overwrite) - # restore files to /structs - restore_directory(restorezip, 'structs', structs_dir, overwrite) + # backup files from /structs + restore_directory(restorezip, DIR_STRUCTS, structs_dir, overwrite) - # restore files to /functions - restore_directory(restorezip, 'functions', uf_dir, overwrite) + # backup files from /functions + restore_directory(restorezip, DIR_UF, uf_dir, overwrite) # restore files to /var/esphome/config restore_directory(restorezip, 'esphome_config', esphome_conf_dir, overwrite) # mark zip-file as restored - logger.info(f"- marking zip-file as restored") + logger.info("- marking zip-file as restored") os.rename(restorezip_filename, restorezip_filename + '.done') # delete last backup timestamp @@ -359,7 +354,7 @@ def restore_backup(conf_base_dir, base_dir, config_etc=False): except OSError: pass - logger.info(f"Finished restore of configuration") + logger.info("Finished restore of configuration") return restorezip_filename @@ -383,7 +378,7 @@ def restore_file(restorezip, arc_dir, filename, dest_dir, overwrite=False): # copy file (taken from zipfile's extract) zip_info = restorezip.getinfo(os.path.join(arc_dir, filename)) - if not(zip_info.filename[-1] == '/'): + if not zip_info.filename[-1] == '/': zip_info.filename = os.path.basename(zip_info.filename) restorezip.extract(zip_info, path=dest_dir, pwd=None) @@ -392,9 +387,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 @@ -406,9 +398,8 @@ def restore_directory(restorezip, arc_dir, dest_dir, overwrite=False): """ logger.info(f"- Restoring directory {dest_dir}") for fn in restorezip.namelist(): - if fn.startswith(arc_dir+'/'): + if fn.startswith(arc_dir + '/'): restore_file(restorezip, arc_dir, os.path.basename(fn), dest_dir, overwrite) - return def write_lastbackuptime(timestamp, timefile): @@ -420,4 +411,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..11c6c358a3 100644 --- a/lib/constants.py +++ b/lib/constants.py @@ -91,6 +91,32 @@ 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' +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, BASE_STRUCT, BASE_HOLIDAY, BASE_ADMIN) +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..1abf76bccf 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, DIR_ETC) logger = logging.getLogger(__name__) @@ -48,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): @@ -85,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, 'struct.yaml', '') + 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('struct_') and filename.endswith('.yaml'): - key_prefix = 'my.' + filename[7:-5] + if filename.startswith(BASE_STRUCT + '_') and filename.endswith(YAML_FILE): + key_prefix = 'my.' + filename[len(BASE_STRUCT):-len(YAML_FILE)] 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'): - dest_fn = filename[7:] + if filename.startswith(BASE_STRUCT + '_') and filename.endswith(YAML_FILE): + dest_fn = filename[len(BASE_STRUCT):] 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._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) 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] + key_prefix = 'my.' + filename[:-len(YAML_FILE)] 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..f200dfdb3d 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, DIR_ETC, DIR_VAR logs_instance = None @@ -67,13 +68,15 @@ def __init__(self, sh): return - def configure_logging(self, config_filename='logging.yaml'): + 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) @@ -140,7 +143,7 @@ def configure_logging(self, config_filename='logging.yaml'): 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 @@ -237,15 +240,18 @@ def return_logs(self): # --------------------------------------------------------------------------- - def load_logging_config(self, filename='logging', 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 conf_filename.endswith('.yaml') and not conf_filename.endswith('.default'): - conf_filename += '.yaml' + 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) return result @@ -257,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(), 'logging') + conf_filename = self._sh.get_config_file(BASE_LOG) shyaml.yaml_save_roundtrip(conf_filename, logging_config, create_backup=create_backup) return @@ -269,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(), 'logging') + 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))) @@ -787,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/logic.py b/lib/logic.py index 1179237c05..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) +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) @@ -508,7 +509,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,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(), '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(), '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(), '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(), '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(), '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(), '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(), '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 diff --git a/lib/metadata.py b/lib/metadata.py index 853c810ab5..b945591160 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) from lib.model.sdp.globals import SDP_VERSION META_MODULE_PARAMETER_SECTION = 'parameters' @@ -39,23 +39,22 @@ META_PLUGIN_FUNCTION_SECTION = 'plugin_functions' META_STRUCT_SECTION = 'item_structs' -#META_DATA_TYPES=['bool', 'int', 'float','num', 'scene', 'str', ['list','list(subtype)'], 'dict', 'ip', 'ipv4', 'ipv6', 'mac', 'knx_ga', 'foo'] -#META_DATA_DEFAULTS={'bool': False, 'int': 0, 'float': 0.0, 'scene': 0, 'str': '', 'list': [], 'dict': {}, 'OrderedDict': {}, 'num': 0, 'scene': 0, 'ip': '0.0.0.0', 'ipv4': '0.0.0.0', 'mac': '00:00:00:00:00:00', 'knx_ga': '', 'foo': None} +# META_DATA_TYPES=['bool', 'int', 'float','num', 'scene', 'str', ['list','list(subtype)'], 'dict', 'ip', 'ipv4', 'ipv6', 'mac', 'knx_ga', 'foo'] +# META_DATA_DEFAULTS={'bool': False, 'int': 0, 'float': 0.0, 'scene': 0, 'str': '', 'list': [], 'dict': {}, 'OrderedDict': {}, 'num': 0, 'scene': 0, 'ip': '0.0.0.0', 'ipv4': '0.0.0.0', 'mac': '00:00:00:00:00:00', 'knx_ga': '', 'foo': None} logger = logging.getLogger(__name__) - # global variables to take definitions of multiple plugins all_itemdefinitions = {} all_itemprefixdefinitions = {} all_prefixes_tuple = None + class Metadata(): _version = '?' - def __init__(self, sh, addon_name, addon_type, classpath=''): """ Initialzes the metadata for an addon (plugin or module) from the definition file @@ -78,21 +77,21 @@ def __init__(self, sh, addon_name, addon_type, classpath=''): self._log_premsg = "{} '{}': ".format(addon_type, self._addon_name) -# logger.warning(self._log_premsg+"classpath = '{}'".format( 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 ) + self.relative_filename = os.path.join(addon_type_dir, self._addon_name, addon_type + YAML_FILE) else: - self.relative_filename = os.path.join( classpath.replace('.', os.sep), addon_type+YAML_FILE ) -# logger.warning(self._log_premsg+"relative_filename = '{}'".format( self.relative_filename ) ) + self.relative_filename = os.path.join(classpath.replace('.', os.sep), addon_type + YAML_FILE) +# logger.warning(self._log_premsg + "relative_filename = '{}'".format(self.relative_filename)) # read complete definitions from metadata file - filename = os.path.join( self._sh.get_basedir(), self.relative_filename ) + filename = os.path.join(self._sh.get_basedir(), self.relative_filename) self.meta = shyaml.yaml_load(filename, ordered=True) self.parameters = None @@ -157,11 +156,11 @@ def __init__(self, sh, addon_name, addon_type, classpath=''): self.parameters[param_name]['_name'] = param_name self.parameters[param_name]['_type'] = 'parameter' self._paramlist = list(self.parameters.keys()) - logger.info(self._log_premsg+"Metadata paramlist = '{}'".format( str(self._paramlist) ) ) - if self.parameters is not None: + logger.info(self._log_premsg + "Metadata paramlist = '{}'".format(str(self._paramlist))) + if self.parameters is not None: self._test_definitions(self._paramlist, self.parameters) else: - logger.debug(self._log_premsg+"has no parameter definitions in metadata") + logger.debug(self._log_premsg + "has no parameter definitions in metadata") # test validity of item definition section if self.itemdefinitions is not None: @@ -169,11 +168,11 @@ def __init__(self, sh, addon_name, addon_type, classpath=''): self.itemdefinitions = None else: self._itemdeflist = list(self.itemdefinitions.keys()) - logger.info(self._log_premsg+"Metadata itemdeflist = '{}'".format( str(self._itemdeflist) ) ) - if self.itemdefinitions is not None: + logger.info(self._log_premsg + "Metadata itemdeflist = '{}'".format(str(self._itemdeflist))) + if self.itemdefinitions is not None: self._test_definitions(self._itemdeflist, self.itemdefinitions) else: - logger.debug(self._log_premsg+"has no item definitions in metadata") + logger.debug(self._log_premsg + "has no item definitions in metadata") # test validity of item-prefix definition section if self.itemprefixdefinitions is not None: @@ -181,11 +180,11 @@ def __init__(self, sh, addon_name, addon_type, classpath=''): self.itemprefixdefinitions = None else: self._itemprefixdeflist = list(self.itemprefixdefinitions.keys()) - logger.info(self._log_premsg+"Metadata itemprefixdeflist = '{}'".format( str(self._itemprefixdeflist) ) ) - if self.itemprefixdefinitions is not None: + logger.info(self._log_premsg + "Metadata itemprefixdeflist = '{}'".format(str(self._itemprefixdeflist))) + if self.itemprefixdefinitions is not None: self._test_definitions(self._itemprefixdeflist, self.itemprefixdefinitions) else: - logger.debug(self._log_premsg+"has no item definitions in metadata") + logger.debug(self._log_premsg + "has no item definitions in metadata") # build dict for checking of item attributes and their values if self.itemdefinitions is not None: @@ -212,11 +211,11 @@ def __init__(self, sh, addon_name, addon_type, classpath=''): self.logic_parameters = None else: self._logic_paramlist = list(self.logic_parameters.keys()) - logger.info(self._log_premsg+"Metadata logic_paramlist = '{}'".format( str(self._logic_paramlist) ) ) - if self.logic_parameters is not None: + logger.info(self._log_premsg + "Metadata logic_paramlist = '{}'".format(str(self._logic_paramlist))) + if self.logic_parameters is not None: self._test_definitions(self._logic_paramlist, self.logic_parameters) else: - logger.debug(self._log_premsg+"has no logic-parameter definitions in metadata") + logger.debug(self._log_premsg + "has no logic-parameter definitions in metadata") # test validity of plugin-function definition section if self.plugin_functions is not None: @@ -224,8 +223,8 @@ def __init__(self, sh, addon_name, addon_type, classpath=''): self.plugin_functions = None else: self._plugin_functionlist = list(self.plugin_functions.keys()) - logger.info(self._log_premsg+"Metadata plugin_functionlist = '{}'".format( str(self._plugin_functionlist) ) ) - if self.plugin_functions is not None: + logger.info(self._log_premsg + "Metadata plugin_functionlist = '{}'".format(str(self._plugin_functionlist))) + if self.plugin_functions is not None: # self._test_definitions(self._plugin_functionlist, self.plugin_functions) pass dummy = self.get_plugin_function_defstrings(with_type=False, with_default=False) @@ -233,7 +232,7 @@ def __init__(self, sh, addon_name, addon_type, classpath=''): dummy = self.get_plugin_function_defstrings(with_type=False, with_default=True) dummy = self.get_plugin_function_defstrings(with_type=True, with_default=True) else: - logger.debug(self._log_premsg+"has no plugin-function definitions in metadata") + logger.debug(self._log_premsg + "has no plugin-function definitions in metadata") # test validity of structs definition section @@ -250,7 +249,7 @@ def __init__(self, sh, addon_name, addon_type, classpath=''): # self.itemstructs[struct][i][si] = dict(self.itemstructs[struct][i][si]) # logger.info(self._log_premsg + "Metadata itemstruct '{}' = '{}'".format(struct, dict(self.itemstructs[struct]))) # if self.itemstructs is not None: -## self._test_definitions(self._itemdeflist, self.itemdefinitions) +# self._test_definitions(self._itemdeflist, self.itemdefinitions) # pass else: logger.info(self._log_premsg + "has no item-struct definitions in metadata") @@ -337,7 +336,7 @@ def _test_definitions(self, definition_list, definition_dict): Test parameter or item-attribute definitions for validity """ definition_list = list(definition_dict.keys()) -# logger.warning(self._log_premsg+"Metadata definition_list = '{}'".format( definition_list ) ) +# logger.warning(self._log_premsg + "Metadata definition_list = '{}'".format(definition_list)) for definition in definition_list: if definition_dict[definition] is not None: typ = str(definition_dict[definition].get('type', FOO)).lower() @@ -345,11 +344,11 @@ def _test_definitions(self, definition_list, definition_dict): definition_dict[definition]['listtype'] = [FOO] definition_dict[definition]['listlen'] = 0 if definition_dict[definition].get('type', FOO) == 'list': - logger.debug(self._log_premsg+"definition = '{}' of type '{}'".format(definition, str(definition_dict[definition].get('type', FOO)).lower() ) ) + logger.debug(self._log_premsg + "definition = '{}' of type '{}'".format(definition, str(definition_dict[definition].get('type', FOO)).lower())) if not (typ in META_DATA_TYPES): # test for list with specified datatype if typ.startswith('list(') and typ.endswith(')'): - logger.debug(self._log_premsg+"definition = '{}' of type '{}'".format(definition, str(definition_dict[definition].get('type', FOO)).lower() ) ) + logger.debug(self._log_premsg + "definition = '{}' of type '{}'".format(definition, str(definition_dict[definition].get('type', FOO)).lower())) definition_dict[definition]['type'] = 'list' listparam = typ[5:] listparam = listparam[:-1].strip().split(',') @@ -370,21 +369,21 @@ def _test_definitions(self, definition_list, definition_dict): listparam2.append(listparam[i].strip()) else: listparam2.append(FOO) - logger.error(self._log_premsg+"definition = '{}': Invalid subtype '{}' specified, using '{}' instead (META_DATA_TYPES={})".format(definition, listparam[i], FOO, META_DATA_TYPES)) + logger.error(self._log_premsg + "definition = '{}': Invalid subtype '{}' specified, using '{}' instead (META_DATA_TYPES={})".format(definition, listparam[i], FOO, META_DATA_TYPES)) listparam = listparam2 definition_dict[definition]['listtype'] = listparam else: - logger.error(self._log_premsg+"Invalid definition in metadata file '{}': type '{}' for parameter '{}' -> using type '{}' instead".format( self.relative_filename, typ, definition, FOO ) ) + logger.error(self._log_premsg + "Invalid definition in metadata file '{}': type '{}' for parameter '{}' -> using type '{}' instead".format(self.relative_filename, typ, definition, FOO)) definition_dict[definition]['type'] = FOO if definition_dict[definition].get('type', FOO) == 'list': - logger.debug(self._log_premsg+"definition = '{}' list of subtype_list = {}, listlen={}".format(definition, definition_dict[definition]['listtype'], definition_dict[definition]['listlen'] ) ) + logger.debug(self._log_premsg + "definition = '{}' list of subtype_list = {}, listlen={}".format(definition, definition_dict[definition]['listtype'], definition_dict[definition]['listlen'])) else: - logger.debug(self._log_premsg+"definition = '{}' list of listparam = >{}<, listlen={}".format(definition, definition_dict[definition]['listtype'], definition_dict[definition]['listlen'] ) ) + logger.debug(self._log_premsg + "definition = '{}' list of listparam = >{}<, listlen={}".format(definition, definition_dict[definition]['listtype'], definition_dict[definition]['listlen'])) else: - logger.info(self._log_premsg+"definition = '{}'".format( definition ) ) + logger.info(self._log_premsg + "definition = '{}'".format(definition)) return def _strip_quotes(self, string): @@ -606,7 +605,7 @@ def _test_valuetype(self, typ, subtype, value): """ Returns True, if the value can be converted to the specified type """ -# logger.warning(self._log_premsg+"_test_valuetype-list: typ={}, subtype={}, value={}".format(typ, subtype, value)) +# logger.warning(self._log_premsg + "_test_valuetype-list: typ={}, subtype={}, value={}".format(typ, subtype, value)) if typ == 'bool': return Utils.to_bool(value, default='?') != '?' elif typ == 'int': @@ -704,7 +703,7 @@ def _expand_listvalues(self, value, definition): if (typ == 'list') and (not isinstance(value, list)): result = Utils.string_to_list(value) # if (typ == 'list'): -# logger.warning(self._log_premsg+"_expand_listvalues: value = >{}<, type(value) = >{}<, result = >{}<, type(result) = >{}<".format(value, type(value), result, type(result))) +# logger.warning(self._log_premsg + "_expand_listvalues: value = >{}<, type(value) = >{}<, result = >{}<, type(result) = >{}<".format(value, type(value), result, type(result))) return result @@ -741,7 +740,7 @@ def _convert_valuetotype(self, typ, value): elif typ == FOO: result = value else: - logger.error(self._log_premsg+"unhandled type {}".format(typ)) + logger.error(self._log_premsg + "unhandled type {}".format(typ)) return result @@ -760,9 +759,9 @@ def _convert_value(self, value, definition, is_default=False): if result != orig: # Für non-default Prüfung nur Warning if is_default: - logger.error(self._log_premsg+f"Invalid default '{orig}' in metadata file '{self.relative_filename}' for {definition['_type']} '{definition['_name']}' -> using '{result}' instead" ) + logger.error(self._log_premsg+f"Invalid default '{orig}' in metadata file '{self.relative_filename}' for {definition['_type']} '{definition['_name']}' -> using '{result}' instead") else: - logger.warning(self._log_premsg+f"Invalid value '{orig}' for {definition['_type']} '{definition['_name']}' -> using '{result}' instead {definition.get('_def_in', '')}" ) + logger.warning(self._log_premsg+f"Invalid value '{orig}' for {definition['_type']} '{definition['_name']}' -> using '{result}' instead {definition.get('_def_in', '')}") return result @@ -826,7 +825,7 @@ def _test_validity(self, param, value, definition=None, is_default=False): elif definition.get('type', 'foo') in ['list']: if definition['listlen'] > 0: if definition['listlen'] != len(value): - logger.warning(self._log_premsg+"Invalid value '{}' in plugin configuration file for parameter '{}' -> length of list is not {}".format( value, param, self.parameters[param]['listlen'] ) ) + logger.warning(self._log_premsg + "Invalid value '{}' in plugin configuration file for parameter '{}' -> length of list is not {}".format(value, param, self.parameters[param]['listlen'])) while len(value) < definition['listlen']: value.append('') result = value @@ -861,13 +860,13 @@ def _test_validity(self, param, value, definition=None, is_default=False): elif self.parameters[param].get('type') in ['list']: if self.parameters[param]['listlen'] > 0: if self.parameters[param]['listlen'] != len(value): - logger.warning(self._log_premsg+"Invalid value '{}' in plugin configuration file for parameter '{}' -> length of list is not {}".format( value, param, self.parameters[param]['listlen'] ) ) + logger.warning(self._log_premsg + "Invalid value '{}' in plugin configuration file for parameter '{}' -> length of list is not {}".format(value, param, self.parameters[param]['listlen'])) while len(value) < self.parameters[param]['listlen']: value.append('') result = value if self.parameters[param] is None: - logger.warning(self._log_premsg+"_test_validity: param {}".format(param)) + logger.warning(self._log_premsg + "_test_validity: param {}".format(param)) else: # test against list of valid entries result = self._test_against_valid_list(self.parameters[param], result) @@ -1085,7 +1084,7 @@ def _get_definition_defaultvalue(self, definition, definitions, definitionlist): ###ms if not self._test_value(value, self.parameters[definition]): # Für non-default Prüfung nur Warning - logger.error(self._log_premsg+"Invalid data for type '{}' in metadata file '{}': default '{}' for parameter '{}' -> using '{}' instead".format( definitions[definition].get('type'), self.relative_filename, value, definition, self._get_default_if_none(typ) ) ) + logger.error(self._log_premsg + "Invalid data for type '{}' in metadata file '{}': default '{}' for parameter '{}' -> using '{}' instead".format(definitions[definition].get('type'), self.relative_filename, value, definition, self._get_default_if_none(typ))) value = None if value is None: value = self._get_default_if_none(typ) @@ -1095,7 +1094,7 @@ def _get_definition_defaultvalue(self, definition, definitions, definitionlist): value = self._test_validity('', value, self.parameters[definition], is_default=True) if value != orig_value: # Für non-default Prüfung nur Warning - logger.error(self._log_premsg+"Invalid default '{}' in metadata file '{}' for parameter '{}' -> using '{}' instead".format( orig_value, self.relative_filename, definition, value ) ) + logger.error(self._log_premsg + "Invalid default '{}' in metadata file '{}' for parameter '{}' -> using '{}' instead".format(orig_value, self.relative_filename, definition, value)) return value @@ -1159,10 +1158,10 @@ def check_parameters(self, args): addon_params = collections.OrderedDict() hide_params = collections.OrderedDict() if self.meta is None: - logger.info(self._log_premsg+"No metadata found" ) + logger.info(self._log_premsg + "No metadata found") return (addon_params, True, hide_params) if self.parameters is None: - logger.info(self._log_premsg+"No parameter definitions found in metadata" ) + logger.info(self._log_premsg + "No parameter definitions found in metadata") return (addon_params, True, hide_params) allparams_ok = True @@ -1172,13 +1171,13 @@ def check_parameters(self, args): if value is None: if self.parameters[param] is not None: if self.parameters[param].get('mandatory'): - logger.error(self._log_premsg+"'{}' is mandatory, but was not found in /etc/{}".format(param, self._addon_type+YAML_FILE)) + logger.error(self._log_premsg + "'{}' is mandatory, but was not found in /etc/{}".format(param, self._addon_type + YAML_FILE)) allparams_ok = False else: addon_params[param] = self.get_parameter_defaultvalue(param) hide_params[param] = Utils.to_bool(self.parameters[param].get('hide'), default=False) - logger.info(self._log_premsg+"value not found in plugin configuration file for parameter '{}' -> using default value '{}' instead".format(param, addon_params[param] ) ) - # logger.warning(self._log_premsg+"'{}' not found in /etc/{}, using default value '{}'".format(param, self._addon_type+YAML_FILE, addon_params[param])) + logger.info(self._log_premsg + "value not found in plugin configuration file for parameter '{}' -> using default value '{}' instead".format(param, addon_params[param])) + # logger.warning(self._log_premsg + "'{}' not found in /etc/{}, using default value '{}'".format(param, self._addon_type + YAML_FILE, addon_params[param])) else: value = self._expand_listvalues(value, self.parameters[param]) if self._test_value(value, self.parameters[param]): @@ -1188,16 +1187,16 @@ def check_parameters(self, args): hide_params[param] = None else: hide_params[param] = Utils.to_bool(self.parameters[param].get('hide'), default=False) - logger.debug(self._log_premsg+"Found '{}' with value '{}' in /etc/{}".format(param, value, self._addon_type+YAML_FILE)) + logger.debug(self._log_premsg + "Found '{}' with value '{}' in /etc/{}".format(param, value, self._addon_type + YAML_FILE)) else: if self.parameters.get(param) is not None: if bool(self.parameters[param].get('mandatory', False)) is True: - logger.error(self._log_premsg+"'{}' is mandatory, but no valid value was found in /etc/{}".format(param, self._addon_type+YAML_FILE)) + logger.error(self._log_premsg + "'{}' is mandatory, but no valid value was found in /etc/{}".format(param, self._addon_type + YAML_FILE)) allparams_ok = False else: addon_params[param] = self.get_parameter_defaultvalue(param) hide_params[param] = Utils.to_bool(self.parameters[param].get('hide'), default=False) - logger.error(self._log_premsg+"Found invalid value '{}' for parameter '{}' (type {}) in /etc/{}, using default value '{}' instead".format(value, param, self.parameters[param]['type'], self._addon_type+YAML_FILE, str(addon_params[param]))) + logger.error(self._log_premsg + "Found invalid value '{}' for parameter '{}' (type {}) in /etc/{}, using default value '{}' instead".format(value, param, self.parameters[param]['type'], self._addon_type + YAML_FILE, str(addon_params[param]))) return (addon_params, allparams_ok, hide_params) @@ -1313,8 +1312,8 @@ def _version_to_list(self, vers): # get rid of non numeric parts vlist = [] for v in vsplit: - #v = re.findall('\d+', v )[0] - v = re.findall(r'\d+', v )[0] + #v = re.findall('\d+', v)[0] + v = re.findall(r'\d+', v)[0] vi = 0 try: vi = int(v) diff --git a/lib/module.py b/lib/module.py index 3ea7f3dbe2..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) +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, '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: @@ -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 458530d9af..00ef96a7b7 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 @@ -322,7 +322,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..d56e99f3a2 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, DIR_SCENES) logger = logging.getLogger(__name__) @@ -81,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 @@ -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..38f7df46c4 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 @@ -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, 'holidays'+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 c407c4fb9f..fd2afe3ff1 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, 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 @@ -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,28 +162,32 @@ 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) + 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, 'env' + os.path.sep) - 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._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) + # 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) # 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): @@ -534,33 +538,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): diff --git a/lib/userfunctions.py b/lib/userfunctions.py index 6f410083e9..a3efa3429e 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 @@ -92,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/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/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 255aa5b4a1..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,15 +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.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): """ @@ -112,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() @@ -134,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) @@ -149,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)) @@ -175,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) @@ -337,7 +337,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/modules/admin/api_logics.py b/modules/admin/api_logics.py index 1db6703428..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,9 +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.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() @@ -61,7 +64,6 @@ def __init__(self, module): self.logics_data = {} self.logics = Logics.get_instance() - return def get_body(self, text=False): @@ -116,7 +118,6 @@ def logics_initialize(self): self.blockly_plugin_loaded = True except: pass - return def fill_logicdict(self, logicname): """ @@ -282,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': @@ -318,7 +318,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 +330,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 +347,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): """ @@ -403,9 +412,9 @@ 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 = '#!/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}'"}) @@ -430,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)} @@ -464,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/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 diff --git a/logics/logic.tpl b/templates/logic.tpl similarity index 100% rename from logics/logic.tpl rename to templates/logic.tpl diff --git a/functions/uf.tpl b/templates/uf.tpl similarity index 100% rename from functions/uf.tpl rename to templates/uf.tpl 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)))